EDA
Distribution of
Individual Features
The distributions of the individual features in the NHANES dataset
provide a snapshot of the characteristics of the population in terms of
various health and demographic variables. Understanding these
distributions is essential for identifying patterns, detecting potential
biases, and assessing the range and variability of each feature. This
analysis helps in understanding how variables such as age, body weight,
blood pressure, and lifestyle factors are spread across the dataset,
which is crucial for subsequent statistical modeling and
interpretation.
# Convert variables to factors with proper labels
nhanes$sex <- factor(nhanes$sex, levels = c(0, 1), labels = c("Female", "Male"))
nhanes$race <- factor(nhanes$race, levels = c(1, 2, 3), labels = c("White", "Black", "Other"))
nhanes$past.smk <- factor(nhanes$past.smk, levels = c(1, 2), labels = c("Yes", "No"))
nhanes$current.smk <- factor(nhanes$current.smk, levels = c(1, 2), labels = c("Yes", "No"))
# Create frequency tables
sex_count <- table(nhanes$sex)
race_count <- table(nhanes$race)
past.smk_count <- table(nhanes$past.smk)
current.smk_count <- table(nhanes$current.smk)
par(mfrow = c(2, 2))
# Plot the bar charts
barplot(height = sex_count, col = "steelblue", main = "Sex", xlab = "Sex", ylab = "Count")
barplot(height = race_count, col = "steelblue", main = "Race", xlab = "Race", ylab = "Count")
barplot(height = past.smk_count, col = "steelblue", main = "Past Smoker", xlab = "Past Smoker", ylab = "Count")
barplot(height = current.smk_count, col = "steelblue", main = "Current Smoker", xlab = "Current Smoker", ylab = "Count")

There are slightly more male observations than female observation in
this data set. The majority of participants are white, followed by
black. All participants are recorded as being past smokers, which is
likely an error. There is an imbalance for past smoker. In turn, past
smoker cannot be used for analysis. About half of current participants
are current smokers.
par(mfrow = c(2, 2))
# Histogram for Cholesterol
hist(nhanes$cholestrol,
main = "Histogram of Cholesterol",
xlab = "Cholesterol",
col = "steelblue",
border = "black",
breaks = 20) # Control number of bins
# Histogram for Systolic BP
hist(nhanes$avg.sbp,
main = "Histogram of Systolic BP",
xlab = "Systolic BP",
col = "steelblue",
border = "black",
breaks = 20)
# Histogram for Body Weight
hist(nhanes$body.weight,
main = "Histogram of Body Weight",
xlab = "Body Weight",
col = "steelblue",
border = "black",
breaks = 20)
# Histogram for Height
hist(nhanes$body.weight,
main = "Histogram of Height",
xlab = "Height",
col = "steelblue",
border = "black",
breaks = 20)

Most participants do not have high blood pressure. The age of
participants varies from about 20 to about 90. The height distribution
is roughly symmetric. The body weight distribution is skewed right due
to some high outliers. The high outliers may indicate obesity.
par(mfrow = c(2,2))
hist(nhanes$avg.sbp, col = "steelblue",
main = "Systolic BP",
xlab = "Systolic BP", ylab = "Frequency",
breaks = 30)
hist(nhanes$avg.dbp, col = "steelblue",
main = "Diastolic BP",
xlab = "Diastolic BP", ylab = "Frequency",
breaks = 30)
hist(nhanes$cholestrol, col = "steelblue",
main = "Cholesterol",
xlab = "Cholesterol", ylab = "Frequency",
breaks = 30)

Systolic BP and cholesterol have skewed right distributions.
Diastolic BP is roughly symmetric. Of the 7927 participants, 1964 are
obese.
Relationship between
Features
Examining relationships between variables using scatterplots and a
correlation matrix provides a comprehensive approach to understanding
associations. Scatterplots visually reveal how two continuous variables
interact, allowing us to identify trends, correlations, and potential
outliers. Meanwhile, a correlation matrix quantifies the strength and
direction of relationships between multiple variables at once, helping
to pinpoint which pairs are strongly correlated. Together, scatterplots
and a correlation matrix offer valuable insights into the underlying
patterns in the data, guiding further statistical analysis and model
selection.
# Set seed for reproducibility
set.seed(123)
# Sample 500 rows (adjust the number as needed)
sample_indices <- sample(nrow(nhanes), size = 500, replace = TRUE)
sampled_data <- nhanes[sample_indices, c("age", "body.weight", "height", "avg.sbp", "avg.dbp", "cholestrol")]
# Create a pairwise scatterplot matrix with the sampled data
pairs(sampled_data,
main = "Pairwise Scatterplots (Bootstrapped Sample)",
pch = 19, # Shape of the points (circle)
col = rgb(0, 0, 1, 0.5) # Color with transparency (blue)
)

nhanes_subset <- nhanes[, c("age", "body.weight", "height", "avg.sbp", "avg.dbp", "cholestrol")]
# Compute the correlation matrix
cor_matrix <- cor(nhanes_subset, use = "pairwise.complete.obs")
# Print the correlation matrix
print(cor_matrix)
age body.weight height avg.sbp avg.dbp cholestrol
age 1.00000000 -0.01377966 -0.08538971 0.55641362 0.1143690 0.2738272
body.weight -0.01377966 1.00000000 0.45314709 0.13693751 0.2900425 0.1032280
height -0.08538971 0.45314709 1.00000000 0.01379185 0.2060106 -0.0631896
avg.sbp 0.55641362 0.13693751 0.01379185 1.00000000 0.5213667 0.2306877
avg.dbp 0.11436905 0.29004249 0.20601064 0.52136671 1.0000000 0.1706567
cholestrol 0.27382718 0.10322800 -0.06318960 0.23068766 0.1706567 1.0000000
corrplot(cor_matrix, method = "color", type = "upper", tl.col = "black", tl.cex = 0.8)

The most highly correlate features are age and average systolic blood
pressure, body weight and height, average systolic blood pressure and
average diastolic blood pressure, age and cholesterol, body weight and
cholesterol, and body weigh and average diastolic blood pressure. The
pairwise relationships are displayed with individual scatter plots.
# Set seed for reproducibility
set.seed(123)
# Bootstrap a smaller sample (adjust size as needed)
sample_indices <- sample(nrow(nhanes), size = 500, replace = TRUE)
sampled_data <- nhanes[sample_indices, ]
# Set up a 2x3 plotting grid
par(mfrow = c(2, 3))
# Scatterplot for Age and Avg. Systolic Blood Pressure
plot(sampled_data$age, sampled_data$avg.sbp, main = "Age vs Avg. Systolic BP",
xlab = "Age", ylab = "Avg. Systolic BP", col = "blue", pch = 19)
# Scatterplot for Body Weight and Height
plot(sampled_data$body.weight, sampled_data$height, main = "Body Weight vs Height",
xlab = "Body Weight", ylab = "Height", col = "red", pch = 19)
# Scatterplot for Avg. Systolic Blood Pressure and Avg. Diastolic Blood Pressure
plot(sampled_data$avg.sbp, sampled_data$avg.dbp, main = "Avg. Systolic vs Avg. Diastolic BP",
xlab = "Avg. Systolic BP", ylab = "Avg. Diastolic BP", col = "forestgreen", pch = 19)
# Scatterplot for Age and Cholesterol
plot(sampled_data$age, sampled_data$cholestrol, main = "Age vs Cholesterol",
xlab = "Age", ylab = "Cholesterol", col = "purple", pch = 19)
# Scatterplot for Avg. Systolic BP and Cholesterol
plot(sampled_data$avg.sbp, sampled_data$cholestrol, main = "Avg Systolic BP vs Cholesterol",
xlab = "Cholesterol", ylab = "Avg.SBP", col = "gold", pch = 19)
# Scatterplot for Body Weight and Avg. Diastolic Blood Pressure
plot(sampled_data$body.weight, sampled_data$avg.dbp, main = "Body Weight vs Avg. Diastolic BP",
xlab = "Body Weight", ylab = "Avg. Diastolic BP", col = "maroon", pch = 19)

Six of the most correlated pairwise relationship are visualized in
scatter plots. All pairwise relationships are linear with no apparent
clustering.
# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))
# Create a contingency table
sex_hbp_table <- table(nhanes$sex, nhanes$hbp)
# Generate mosaic plot
mosaicplot(sex_hbp_table,
col = c("gold", "royalblue"),
main = "Sex and High Blood Pressure",
xlab = "Sex Status",
ylab = "HBP Status",
border = "black")
# Create a contingency table
race_hbp_table <- table(nhanes$race, nhanes$hbp)
# Generate mosaic plot
mosaicplot(race_hbp_table,
col = c("gold", "royalblue"),
main = "Race and High Blood Pressure",
xlab = "Race",
ylab = "HBP Status",
border = "black")
# Create a contingency table
current.smk_hbp_table <- table(nhanes$current.smk, nhanes$hbp)
# Generate mosaic plot
mosaicplot(current.smk_hbp_table,
col = c("gold", "royalblue"),
main = "Current Smoking Status and HBP",
xlab = "Current Smoking Status",
ylab = "HBP Status",
border = "black")
# Create a contingency table
past.smk_hbp_table <- table(nhanes$past.smk, nhanes$hbp)
# Generate mosaic plot
mosaicplot(past.smk_hbp_table,
col = c("gold", "royalblue"),
main = "Past Smoking Status and HBP",
xlab = "Past Smoking Status",
ylab = "HBP Status",
border = "black")

Relationship between high blood pressure and race, sex, and current
smoking status can be visualized in the mosaic plots. There appears to
be an association between high current smoking and race with high blood
pressure. A less pronounced relationship can been seen between sex and
race with high blood pressure.
# Set up a 2x2 plotting grid
par(mfrow = c(1, 2))
# Create a contingency table
current.smk_race_table <- table(nhanes$race, nhanes$current.smk)
# Generate mosaic plot
mosaicplot(current.smk_race_table,
col = c("gold", "royalblue"),
main = "Race and Current Smoke Status",
xlab = "Race",
ylab = "Current Smoke",
border = "black")
# Create a contingency table
current.smk_sex_table <- table(nhanes$sex, nhanes$current.smk)
# Generate mosaic plot
mosaicplot(current.smk_sex_table,
col = c("gold", "royalblue"),
main = "Sex and Current Smoke Status",
xlab = "Sex",
ylab = "Current Smoke",
border = "black")

# Create a contingency table
past.smk_race_table <- table(nhanes$race, nhanes$past.smk)
An association exists between race and current smoking. Black
individuals tend to smoke more than white or other individuals. An
association exists between sex and current smoke status. Female tend to
smoke currently slight more than males in this population.
MISSING VALUES
Imputation for
Categorical Features
The distribution of missing values for is examined to determine any
patterns. This step is done to ensure that value are missing at
random.
# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))
#sum(is.na(nhanes$race))
#mean(is.na(nhanes$race))
#table(nhanes$race, useNA = "ifany")
#ggplot(nhanes, aes(x = factor(race))) +
#geom_bar(fill = "blue") +
#labs(title = "Distribution of Race (Before Imputation)", x = "Race", y = "Count") +
#theme_minimal()
aggr(nhanes, col = c("navyblue", "red"), numbers = TRUE, sortVars = TRUE,
labels = names(nhanes), cex.axis = 0.7, gap = 2,
ylab = c("Missing Data Overview", "Pattern of Missing Data"))

Variables sorted by number of missings:
Variable Count
race 0.1499937
cholestrol 0.1499937
body.weight 0.1400278
X 0.0000000
obs 0.0000000
psu 0.0000000
stratum 0.0000000
stat.weight 0.0000000
age 0.0000000
sex 0.0000000
height 0.0000000
avg.sbp 0.0000000
avg.dbp 0.0000000
past.smk 0.0000000
current.smk 0.0000000
hbp 0.0000000
A pattern of missing values exists between body weight, BMI, and
obese. This pattern can be explained by the fact that body weight is
used to calculate BMI, which is then used to classify observations for
obese. However, the pattern of missing value appears to be random for
the other features.
No pattern of missing values exists between race and other features,
In turn, kNN imputation is used for missing values of race.
sum(is.na(nhanes$race))
[1] 1189
table(nhanes$race, useNA = "ifany") # Check distribution including NAs
White Black Other <NA>
4693 1858 187 1189
# Perform KNN Imputation (single imputation)
nhanes_imputed <- kNN(nhanes, variable = "race", k = 3)
# Keep only the original columns (without extra columns added by kNN)
nhanes_imputed <- nhanes_imputed[, names(nhanes)]
# COMPARE before and after imputation distribution
par(mfrow = c(1,2)) # Split plotting area into two
# Convert race to a factor for better visualization
nhanes$race <- as.factor(nhanes$race)
nhanes_imputed$race <- as.factor(nhanes_imputed$race)
# Create a new column to indicate whether the data is original or imputed
nhanes$source <- "Before Imputation"
nhanes_imputed$source <- "After Imputation"
# Combine both datasets
nhanes_compare <- bind_rows(nhanes, nhanes_imputed)
# Plot the distribution before and after imputation
ggplot(nhanes_compare, aes(x = race, fill = source)) +
geom_bar(position = "dodge") +
labs(title = "Distribution of Race Before and After Imputation",
x = "Race",
y = "Count",
fill = "Dataset") +
theme_minimal()

imputed_nhanes <- nhanes_imputed
The overall distribution of race after imputation changed slightly
after imputation.
Regression-Based
Imputation for Numerical Feature
The pattern of missing values for body weight and cholesterol is
examined for any potential patterns.
#vis_miss(nhanes[, c("body.weight", "cholestrol")])
# Set smaller font size for the plot
par(cex = 0.4) # Adjust this value for desired font size
# Generate the missing data pattern plot
md.pattern(imputed_nhanes)

X obs psu stratum stat.weight age sex race height avg.sbp avg.dbp past.smk
5792 1 1 1 1 1 1 1 1 1 1 1 1
1025 1 1 1 1 1 1 1 1 1 1 1 1
946 1 1 1 1 1 1 1 1 1 1 1 1
164 1 1 1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0 0 0
current.smk hbp source body.weight cholestrol
5792 1 1 1 1 1 0
1025 1 1 1 1 0 1
946 1 1 1 0 1 1
164 1 1 1 0 0 2
0 0 0 1110 1189 2299
# Reset graphics settings (optional)
par(cex = 1)
A pattern of missing values exists between body weight, BMI, and
obese because
Body weight is correlated with height, average diastolic blood
pressure, and sex. In turn, the missing body weight values will be
imputed using a regression model using said explanatory features.
# Subset nhanes to contain only complete observations for selected variables
complete_nhanes <- imputed_nhanes[complete.cases(imputed_nhanes[, c("body.weight", "height", "avg.dbp", "sex")]), ]
# Fit the linear regression model
model_body.weight <- lm(body.weight ~ height + avg.dbp + sex, data = complete_nhanes)
# View the model summary
summary(model_body.weight)
Call:
lm(formula = body.weight ~ height + avg.dbp + sex, data = complete_nhanes)
Residuals:
Min 1Q Median 3Q Max
-86.131 -22.276 -4.259 17.073 269.863
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -207.90214 9.27828 -22.407 < 2e-16 ***
height 4.86582 0.14277 34.080 < 2e-16 ***
avg.dbp 0.76728 0.03913 19.606 < 2e-16 ***
sexMale -7.00000 1.07917 -6.486 9.4e-11 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 33.52 on 6813 degrees of freedom
Multiple R-squared: 0.2495, Adjusted R-squared: 0.2491
F-statistic: 754.9 on 3 and 6813 DF, p-value: < 2.2e-16
# Plot the residuals
plot(model_body.weight$residuals)

The residual plot shows no patterns. In turn, the model fit the data
well and the assumption of constant variance of residuals is
satisfied.
# Create a copy of the dataset to work with
#imputed_nhanes <- complete_nhanes
colSums(is.na(imputed_nhanes))
X obs psu stratum stat.weight age
0 0 0 0 0 0
sex race body.weight height avg.sbp avg.dbp
0 0 1110 0 0 0
past.smk current.smk cholestrol hbp source
0 0 1189 0 0
# Identify the rows with missing values for body.weight
missing_body_weight <- is.na(imputed_nhanes$body.weight)
# Use the model to predict the missing values
imputed_nhanes$body.weight[missing_body_weight] <- predict(model_body.weight,
newdata = imputed_nhanes[missing_body_weight, c("height", "avg.dbp", "sex", "age")])
# Check the dataset
#head(imputed_nhanes)
# Create a plot to overlay the density curves
ggplot() +
geom_density(data = complete_nhanes, aes(x = body.weight), fill = "blue", alpha = 0.2, na.rm = TRUE) +
geom_density(data = imputed_nhanes, aes(x = body.weight), fill = "red", alpha = 0.2, na.rm = TRUE) +
labs(title = "Density Plot of Body Weight: Before and After Imputation",
x = "Body Weight", y = "Density") +
theme_minimal() +
scale_fill_manual(name = "Body Weight", values = c("blue" = "blue", "red" = "red"),
labels = c("Before Imputation", "After Imputation"))

The distribution before and after imputation is similar. In turn, the
imputation did not change the distribution of body weight.
The features that are most correlated to cholesterol are age, average
systolic blood pressure, and body weight. In turn, the missing values of
cholesterol will be imputed with a model using these features.
# Subset nhanes to include only rows with complete cases for cholestrol, age, avg.sbp, and BMI
complete_nhanes <- imputed_nhanes[!is.na(imputed_nhanes$cholestrol) & !is.na(imputed_nhanes$age) &
!is.na(imputed_nhanes$avg.sbp) & !is.na(imputed_nhanes$body.weight), ]
complete_nhanes <- imputed_nhanes[complete.cases(imputed_nhanes[, c("cholestrol", "age", "avg.sbp", "body.weight")]), ]
# Fit the linear regression model to predict cholestrol
model_cholestrol <- lm(cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)
summary(model_cholestrol)
Call:
lm(formula = cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)
Residuals:
Min 1Q Median 3Q Max
-162.16 -28.18 -3.08 24.08 498.54
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 135.10667 3.96327 34.090 < 2e-16 ***
age 0.54613 0.03465 15.762 < 2e-16 ***
avg.sbp 0.21908 0.03267 6.707 2.15e-11 ***
body.weight 0.10341 0.01437 7.198 6.76e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 42.59 on 6734 degrees of freedom
Multiple R-squared: 0.09077, Adjusted R-squared: 0.09037
F-statistic: 224.1 on 3 and 6734 DF, p-value: < 2.2e-16
# View the model summary
summary(model_cholestrol)
Call:
lm(formula = cholestrol ~ age + avg.sbp + body.weight, data = complete_nhanes)
Residuals:
Min 1Q Median 3Q Max
-162.16 -28.18 -3.08 24.08 498.54
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 135.10667 3.96327 34.090 < 2e-16 ***
age 0.54613 0.03465 15.762 < 2e-16 ***
avg.sbp 0.21908 0.03267 6.707 2.15e-11 ***
body.weight 0.10341 0.01437 7.198 6.76e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 42.59 on 6734 degrees of freedom
Multiple R-squared: 0.09077, Adjusted R-squared: 0.09037
F-statistic: 224.1 on 3 and 6734 DF, p-value: < 2.2e-16
# Create a copy of nhanes for imputation
#imputed_nhanes <- nhanes
# Identify rows with missing cholestrol values
missing_cholestrol <- is.na(imputed_nhanes$cholestrol)
# Use the model to predict the missing cholestrol values
imputed_nhanes$cholestrol[missing_cholestrol] <- predict(model_cholestrol,
newdata = imputed_nhanes[missing_cholestrol, c("age", "avg.sbp", "body.weight")])
All explanatory features are significant.
# Get residuals from the model for complete_nhanes
residuals_cholestrol <- residuals(model_cholestrol)
# Predicted values for complete_nhanes
predicted_cholestrol <- predict(model_cholestrol, newdata = complete_nhanes)
# Create a data frame for residuals and predicted values
residuals_data <- data.frame(
predicted_cholestrol = predicted_cholestrol,
residuals_cholestrol = residuals_cholestrol
)
# Create the residual plot
library(ggplot2)
ggplot(residuals_data, aes(x = predicted_cholestrol, y = residuals_cholestrol)) +
geom_point(color = "blue") + # Plot the residuals
geom_hline(yintercept = 0, linetype = "dashed", color = "red") + # Add a horizontal line at 0
labs(title = "Residual Plot for Cholestrol Prediction",
x = "Predicted Cholestrol",
y = "Residuals") +
theme_minimal()

# Create the overlay density plot
ggplot() +
geom_density(data = nhanes, aes(x = cholestrol), fill = "blue", alpha = 0.2, na.rm = TRUE) + # Before imputation
geom_density(data = imputed_nhanes, aes(x = cholestrol), fill = "red", alpha = 0.2, na.rm = TRUE) + # After imputation
labs(title = "Density Plot of Cholestrol: Before and After Imputation",
x = "Cholestrol", y = "Density") +
theme_minimal() +
scale_fill_manual(name = "Cholestrol", values = c("blue" = "blue", "red" = "red"),
labels = c("Before Imputation", "After Imputation"))

The distribution of cholesterol did not change significantly after
imputation. As well, the residual plot does not display any patterns. In
turn, the model fit the data well and the assumption of constant
variance of residuals is satisfied.
Feature Engineer
In turn a feature which calculates BMI is created to determine
observations are classified as obese. According to the CDC (https://www.cdc.gov/nccdphp/dnpao/data-trends-maps/help/npao_dtm/definitions.html),
Adult obesity is defined as body mass index (BMI) ≥ 30.0. Another
feature named obese, based on BMI, is created, where 0 = no and 1 =
yes.
# Create BMI feature
imputed_nhanes$BMI <- (imputed_nhanes$body.weight * 703) / (imputed_nhanes$height * imputed_nhanes$height)
# Round the BMI to the nearest ones place
imputed_nhanes$BMI <- round(imputed_nhanes$BMI, 0)
# Create the obese feature based on BMI
imputed_nhanes$obese <- ifelse(imputed_nhanes$BMI >= 30, 1, 0)
par(mfrow = c(1, 2))
# Histogram for BMI
hist(imputed_nhanes$BMI,
main = "Histogram of BMI",
xlab = "BMI",
col = "steelblue",
border = "black",
breaks = 20)
# bar chart for obese
nhanes$obese <- factor(imputed_nhanes$obese, levels = c(0, 1), labels = c("No", "Yes"))
obese_count <- table(imputed_nhanes$obese)
barplot(height = obese_count, col = "steelblue", main = "Obesity", xlab = "Obesity", ylab = "Count")

# Set up a 2x2 plotting grid
par(mfrow = c(2, 2))
# Create a contingency table
obese_hbp_table <- table(imputed_nhanes$obese, imputed_nhanes$hbp)
# Generate mosaic plot
mosaicplot(obese_hbp_table,
col = c("gold", "royalblue"),
main = "Obesity and High Blood Pressure",
xlab = "Obesity Status",
ylab = "HBP Status",
border = "black")
# Create a contingency table
obese_sex_table <- table(imputed_nhanes$sex, imputed_nhanes$obese)
# Generate mosaic plot
mosaicplot(obese_sex_table,
col = c("gold", "royalblue"),
main = "Sex and Obesity",
xlab = "Obesity Status",
ylab = "Sex",
border = "black")
# Create a contingency table
obese_race_table <- table(imputed_nhanes$race, imputed_nhanes$obese)
# Generate mosaic plot
mosaicplot(obese_race_table,
col = c("gold", "royalblue"),
main = "Race and Obesity",
xlab = "Obesity Status",
ylab = "Race",
border = "black")
# Create a contingency table
obese_smoke_table <- table(imputed_nhanes$current.smk, imputed_nhanes$obese)
# Generate mosaic plot
mosaicplot(obese_smoke_table,
col = c("gold", "royalblue"),
main = "Current Smoking Status and Obesity",
xlab = "Obesity Status",
ylab = "Current Smoking Status",
border = "black")

Multiple
Imputation
Multiple Imputation by Chained Equations (MICE) is a powerful
technique used to handle missing data by creating multiple plausible
imputations for each missing value based on relationships in the
observed data. For body weight, cholesterol, and race, MICE can be
applied to generate accurate imputations, accounting for correlations
between these variables and other features in the dataset. This method
allows for more reliable statistical analyses by reducing bias and
preserving variability in the imputed values, making it especially
useful for handling missing data in incoming new data sets.
nhanes1$race <- as.factor(nhanes1$race)
nhanes1$cholestrol <- as.numeric(nhanes1$cholestrol)
nhanes1$body.weight <- as.numeric(nhanes1$body.weight)
nhanes2 <- nhanes1
# 1 initialization
init <- mice(nhanes2, maxit=0)
print(init$method)
X obs psu stratum stat.weight age
"" "" "" "" "" ""
sex race body.weight height avg.sbp avg.dbp
"" "polyreg" "pmm" "" "" ""
past.smk current.smk cholestrol hbp
"" "" "pmm" ""
# 2 imputation models
imp <- mice(nhanes2, method = c("", "", "", "", "", "", "", "polyreg", "pmm", "", "", "", "", "", "pmm", ""),
maxit=10,
m=1,
seed = 123,
print=F)
complete(imp, action = 1)[1:10,]
X obs psu stratum stat.weight age sex race body.weight height avg.sbp
1 3 9 2 43 19451.83 48 0 1 149.7 61.8 131
2 5 11 2 40 1245.52 48 1 1 155.3 66.2 120
3 6 19 1 35 3860.97 44 1 2 189.6 70.2 133
4 7 34 1 13 5031.70 42 0 2 125.8 62.6 100
5 10 48 1 24 26919.29 56 0 1 239.9 67.6 128
6 12 51 1 28 2730.56 44 1 1 317.9 71.1 130
7 15 55 2 44 804.03 48 1 1 245.6 68.0 155
8 19 70 1 9 2629.39 63 1 2 202.9 67.8 137
9 20 71 1 43 1147.32 37 0 1 151.7 61.7 128
10 22 73 1 29 3991.81 42 1 3 205.0 68.4 148
avg.dbp past.smk current.smk cholestrol hbp
1 73 1 2 236 0
2 70 1 2 260 0
3 85 1 1 187 0
4 67 1 1 216 0
5 73 1 2 156 0
6 86 1 1 162 0
7 91 1 1 212 1
8 68 1 1 186 0
9 70 1 1 209 0
10 83 1 1 267 1
# 3 multiple (iterative) imputations
imp5 <- mice(nhanes2, method = "pmm", m = 5, maxit = 10, seed = 123, print=F)
plot(imp5)

Multiple Imputation by Chained Equations (MICE) will be applied to
handle missing values in the body weight variable. To assess the quality
of the imputations, the Mean Squared Error (MSE) will be calculated and
compared the MSE of the single imputation conducted for body weight
earlier in this analysis, providing a measure of the imputation
accuracy.
model5_bodyweight <- with(imp5, lm(body.weight ~ height + avg.dbp + sex))
summary.stats = summary(model5_bodyweight)
summary(pool(model5_bodyweight))
term estimate std.error statistic df p.value
1 (Intercept) -205.7045484 8.74935482 -23.510825 2473.3145 1.747163e-110
2 height 4.8466598 0.13536721 35.803796 1634.9416 8.934110e-208
3 avg.dbp 0.7523449 0.03941721 19.086712 157.8342 7.501512e-43
4 sex -6.6078962 1.06330598 -6.214482 276.0479 1.892089e-09
beta = summary.stats$estimate[seq(1,15,by=3)] # explicit vector: c(1,4,7,10,13)
beta.var = (summary.stats$std.error[seq(1,15,by=3)])^2
Q = mean(beta)
U = mean(beta.var)
B = var(beta)
T = U + (6/5)*B
pool.se = sqrt(T)
cbind(pool.se.intercept = pool.se)
pool.se.intercept
[1,] 123.2177
The model can be used to predict body weight for incoming new
data.
The MSE for single body weight imputation used earlier in this
analysis is MSE = (RSE)^2 = (33.52)^2 = 1123.6 which is substantially
higher than MSE when using MICE. In turn, MICE is recommended in this
analysis.
Multiple Imputation by Chained Equations (MICE) will be applied to
handle missing values in the cholesterol variable. To assess the quality
of the imputations, the Mean Squared Error (MSE) will be calculated and
compared the MSE of the single imputation conducted for cholesterol
earlier in this analysis, providing a measure of the imputation
accuracy.
model5_chol <- with(imp5, lm(cholestrol ~ age + avg.sbp))
summary.stats = summary(model5_chol)
summary(pool(model5_chol))
term estimate std.error statistic df p.value
1 (Intercept) 148.6912121 3.65965489 40.629845 74.24158 1.827860e-52
2 age 0.5233278 0.03407744 15.357017 236.74859 2.129193e-37
3 avg.sbp 0.2585339 0.03425598 7.547118 63.62530 2.094322e-10
beta = summary.stats$estimate[seq(1,15,by=3)] # explicit vector: c(1,4,7,10,13)
beta.var = (summary.stats$std.error[seq(1,15,by=3)])^2
Q = mean(beta)
U = mean(beta.var)
B = var(beta)
T = U + (6/5)*B
pool.se = sqrt(T)
cbind(pool.se.intercept = pool.se)
pool.se.intercept
[1,] 3.659655
The model can be used to predict cholesterol for incoming new
data.
The Mean Squared Error (MSE) from the Residual Standard Error (RSE)
given in the model output from the cholesterol single imputation method
used early in this analysis is MSE = (42.59)^2 = 1813.9. Thus, the Mean
Squared Error (MSE) = 1813.9.
The MSE using MICE is 3.66 which is substantially less. Therefore,
MICE is recommended in the analysis of this data set.
For the missing values in the race variable, Multiple Imputation by
Chained Equations (MICE) will be applied using multinomial logistic
regression. This method will account for the categorical nature of the
race variable, allowing for appropriate imputation based on the
relationships with other features in the dataset.
model_race <- with(imp5, multinom(race ~ avg.dbp + avg.sbp + cholestrol + hbp))
# weights: 18 (10 variable)
initial value 8708.699612
iter 10 value 5523.235718
final value 5495.930272
converged
# weights: 18 (10 variable)
initial value 8708.699612
iter 10 value 5555.595871
final value 5522.606318
converged
# weights: 18 (10 variable)
initial value 8708.699612
iter 10 value 5555.946819
final value 5529.649391
converged
# weights: 18 (10 variable)
initial value 8708.699612
iter 10 value 5560.173296
final value 5529.439901
converged
# weights: 18 (10 variable)
initial value 8708.699612
iter 10 value 5542.550592
final value 5510.127472
converged
# Pool the results from the imputed datasets
pooled_race <- pool(model_race)
# Get a summary of the pooled results
summary(pooled_race)
term estimate std.error statistic df p.value
1 (Intercept) -1.277932787 0.3325448882 -3.8428881 49.56081 3.468517e-04
2 avg.dbp 0.030728414 0.0030785469 9.9814670 343.89941 9.007490e-21
3 avg.sbp -0.009072042 0.0025775079 -3.5196952 252.21602 5.124609e-04
4 cholestrol -0.004119817 0.0007738102 -5.3240668 28.44201 1.091924e-05
5 hbp 0.148914062 0.1127433356 1.3208236 101.89054 1.895191e-01
6 (Intercept) -2.575346290 0.9149529588 -2.8147308 40.55536 7.498473e-03
7 avg.dbp 0.019877690 0.0080743228 2.4618399 3279.39165 1.387374e-02
8 avg.sbp -0.007708776 0.0071499482 -1.0781583 208.24247 2.822104e-01
9 cholestrol -0.005554712 0.0020814684 -2.6686508 36.04555 1.134283e-02
10 hbp -0.151093362 0.3197542727 -0.4725296 109.96948 6.374852e-01
The model can be used to predict race for incoming new data.
FEATURE SELECTION
Feature selection is a critical step in model building, as it helps
improve model performance by identifying the most relevant predictors
and reducing overfitting. In this analysis, both model-based selection
and regularization-based selection techniques will be used to assess and
retain the most influential features for accurate prediction, ensuring a
more efficient and interpretable model.
A full model is created to predict cholesterol.
#sapply(imputed_nhanes, function(x) length(unique(x)))
#head(imputed_nhanes, 25)
#head(imputed_nhanes)
model_full <- lm(cholestrol ~ age + race + BMI + height + body.weight +
hbp + avg.dbp + avg.sbp + obese,
data = imputed_nhanes)
# Summary of the full model
#summary(model_full)
# Tidy the model summary
model_summary <- tidy(model_full)
# Calculate Mean Squared Error (MSE)
mse <- mean(model_full$residuals^2)
# Calculate R-squared
r2 <- summary(model_full)$r.squared
# Create a data frame for MSE and R-squared
metrics_df <- data.frame(Statistic = c("Mean Squared Error", "R-squared"),
Value = c(mse, r2))
# Print the model summary using kable
kable(model_summary, digits = 3, caption = "Regression Model Summary")
Regression Model Summary
| (Intercept) |
0.043 |
0.017 |
2.447 |
0.014 |
| age |
0.250 |
0.013 |
18.847 |
0.000 |
| raceBlack |
-0.068 |
0.024 |
-2.781 |
0.005 |
| raceOther |
-0.206 |
0.067 |
-3.058 |
0.002 |
| BMI |
-0.122 |
0.082 |
-1.477 |
0.140 |
| height |
-0.190 |
0.049 |
-3.862 |
0.000 |
| body.weight |
0.266 |
0.093 |
2.847 |
0.004 |
| hbp |
-0.066 |
0.042 |
-1.574 |
0.116 |
| avg.dbp |
0.116 |
0.013 |
8.774 |
0.000 |
| avg.sbp |
0.054 |
0.021 |
2.597 |
0.009 |
| obese |
-0.022 |
0.038 |
-0.589 |
0.556 |
# Print MSE and R-squared using kable
kable(metrics_df, digits = 3, caption = "Model Performance Metrics")
Model Performance Metrics
| Mean Squared Error |
0.876 |
| R-squared |
0.124 |
Stepwise model selection is conducted to retain significant
predictors. Selection processes based on AIC and BIC are conducted.
# Stepwise model selection based on AIC
model_stepwise_AIC <- step(model_full, direction = "both", trace = 0)
# Stepwise model selection based on BIC
model_stepwise_BIC <- step(model_full, direction = "both", k = log(nrow(imputed_nhanes)), trace = 0)
# Tidy the summaries for both models
stepwise_AIC_summary <- tidy(model_stepwise_AIC)
stepwise_BIC_summary <- tidy(model_stepwise_BIC)
# Compute MSE for both models
mse_AIC <- mean(model_stepwise_AIC$residuals^2)
mse_BIC <- mean(model_stepwise_BIC$residuals^2)
# Compute R-squared for both models
r2_AIC <- summary(model_stepwise_AIC)$r.squared
r2_BIC <- summary(model_stepwise_BIC)$r.squared
# Create data frames for MSE and R-squared
metrics_AIC_df <- data.frame(Statistic = c("Mean Squared Error", "R-squared"),
Value = c(mse_AIC, r2_AIC))
metrics_BIC_df <- data.frame(Statistic = c("Mean Squared Error", "R-squared"),
Value = c(mse_BIC, r2_BIC))
# Print the stepwise AIC model summary
kable(stepwise_AIC_summary, digits = 3, caption = "Stepwise AIC Regression Model Summary")
Stepwise AIC Regression Model Summary
| (Intercept) |
0.038 |
0.015 |
2.462 |
0.014 |
| age |
0.250 |
0.013 |
18.875 |
0.000 |
| raceBlack |
-0.068 |
0.024 |
-2.792 |
0.005 |
| raceOther |
-0.206 |
0.067 |
-3.062 |
0.002 |
| BMI |
-0.127 |
0.082 |
-1.553 |
0.121 |
| height |
-0.188 |
0.049 |
-3.842 |
0.000 |
| body.weight |
0.264 |
0.093 |
2.829 |
0.005 |
| hbp |
-0.067 |
0.042 |
-1.588 |
0.112 |
| avg.dbp |
0.117 |
0.013 |
8.778 |
0.000 |
| avg.sbp |
0.054 |
0.021 |
2.604 |
0.009 |
# Print performance metrics for AIC model
kable(metrics_AIC_df, digits = 3, caption = "Model Performance Metrics (AIC)")
Model Performance Metrics (AIC)
| Mean Squared Error |
0.876 |
| R-squared |
0.124 |
# Print the stepwise BIC model summary
kable(stepwise_BIC_summary, digits = 3, caption = "Stepwise BIC Regression Model Summary")
Stepwise BIC Regression Model Summary
| (Intercept) |
0.000 |
0.011 |
0.000 |
1 |
| age |
0.271 |
0.011 |
25.433 |
0 |
| height |
-0.116 |
0.012 |
-9.640 |
0 |
| body.weight |
0.122 |
0.012 |
9.908 |
0 |
| avg.dbp |
0.129 |
0.011 |
11.519 |
0 |
# Print performance metrics for BIC model
kable(metrics_BIC_df, digits = 3, caption = "Model Performance Metrics (BIC)")
Model Performance Metrics (BIC)
| Mean Squared Error |
0.879 |
| R-squared |
0.121 |
# Check R-squared value
summary(model_stepwise_AIC)$r.squared
[1] 0.123777
summary(model_stepwise_BIC)$r.squared
[1] 0.1210105
Both AIC and BIC stepwise model selection processes have similar R
squared values. The BIC stepwise model is parsimonious. This model
selects age, height, body weight, and average diastolic blood pressure
as predictors for cholesterol.
Regularization-based selection techniques are useful for improving
model performance and preventing overfitting, especially when you have
many predictors. Two common regularization methods are Ridge regression
and Lasso regression.
# Extract the response and predictors
y <- imputed_nhanes$cholestrol # Response variable
X <- as.matrix(imputed_nhanes[, c("age", "BMI", "height", "body.weight", "avg.dbp", "avg.sbp")])
# Optional: Scale predictors (recommended for Ridge and Lasso)
X_scaled <- scale(X)
# Fit Ridge and Lasso models
ridge_model <- glmnet(X_scaled, y, alpha = 0) # Ridge Regression (alpha = 0)
lasso_model <- glmnet(X_scaled, y, alpha = 1) # Lasso Regression (alpha = 1)
# Ridge cross-validation
cv_ridge <- cv.glmnet(X_scaled, y, alpha = 0)
best_lambda_ridge <- cv_ridge$lambda.min
# Lasso cross-validation
cv_lasso <- cv.glmnet(X_scaled, y, alpha = 1)
best_lambda_lasso <- cv_lasso$lambda.min
# Extract coefficients for Ridge and Lasso at optimal lambda
ridge_coefficients <- as.data.frame(as.matrix(coef(cv_ridge, s = "lambda.min")))
colnames(ridge_coefficients) <- c("Coefficient")
ridge_coefficients$Predictor <- rownames(ridge_coefficients)
lasso_coefficients <- as.data.frame(as.matrix(coef(cv_lasso, s = "lambda.min")))
colnames(lasso_coefficients) <- c("Coefficient")
lasso_coefficients$Predictor <- rownames(lasso_coefficients)
# Predictions for Ridge
pred_ridge <- predict(cv_ridge, X_scaled, s = "lambda.min")
# Predictions for Lasso
pred_lasso <- predict(cv_lasso, X_scaled, s = "lambda.min")
# Calculate RMSE for Ridge
rmse_ridge <- sqrt(mean((pred_ridge - y)^2))
# Calculate RMSE for Lasso
rmse_lasso <- sqrt(mean((pred_lasso - y)^2))
# Calculate R² for Ridge
rss_ridge <- sum((pred_ridge - y)^2) # Residual Sum of Squares
tss_ridge <- sum((y - mean(y))^2) # Total Sum of Squares
r2_ridge <- 1 - rss_ridge / tss_ridge
# Calculate R² for Lasso
rss_lasso <- sum((pred_lasso - y)^2)
tss_lasso <- sum((y - mean(y))^2)
r2_lasso <- 1 - rss_lasso / tss_lasso
# Create a data frame for RMSE and R²
metrics_df <- data.frame(
Model = c("Ridge", "Lasso"),
RMSE = c(rmse_ridge, rmse_lasso),
R2 = c(r2_ridge, r2_lasso)
)
# Print Ridge coefficients using kable
kable(ridge_coefficients, digits = 3, caption = "Ridge Regression Coefficients")
Ridge Regression Coefficients
| (Intercept) |
0.000 |
(Intercept) |
| age |
0.245 |
age |
| BMI |
0.041 |
BMI |
| height |
-0.086 |
height |
| body.weight |
0.071 |
body.weight |
| avg.dbp |
0.109 |
avg.dbp |
| avg.sbp |
0.039 |
avg.sbp |
# Print Lasso coefficients using kable
kable(lasso_coefficients, digits = 3, caption = "Lasso Regression Coefficients")
Lasso Regression Coefficients
| (Intercept) |
0.000 |
(Intercept) |
| age |
0.255 |
age |
| BMI |
0.000 |
BMI |
| height |
-0.113 |
height |
| body.weight |
0.120 |
body.weight |
| avg.dbp |
0.114 |
avg.dbp |
| avg.sbp |
0.030 |
avg.sbp |
# Print RMSE and R² values using kable
kable(metrics_df, digits = 3, caption = "Model Performance Metrics (RMSE & R²)")
Model Performance Metrics (RMSE & R²)
| Ridge |
0.937 |
0.121 |
| Lasso |
0.937 |
0.121 |
The Ridge and Lasso method both had similar R squared values.
Therefore, both models are recommended.
LINEAR REGRESSION
In this regression analysis of the NHANES dataset, three models are
developed using cross validation to predict the outcome variable
cholesterol: a full model including all selected predictors, a stepwise
BIC model for variable selection, and a Ridge regression model to
address multicollinearity. Each model was evaluated based on its
predictive performance, with Mean Squared Error (MSE) and R-squared
(R^2) used as key comparison metrics. By analyzing these models, the aim
is to determine the most accurate and efficient approach for modeling
the data while balancing complexity and predictive power.
set.seed(42)
train_control <- trainControl(method = "cv", number = 10)
Full Model
The full model includes all selected predictor variables, providing a
comprehensive approach to estimating cholesterol levels. To assess its
stability, we implement k-fold cross-validation, which systematically
partitions the dataset into multiple training and validation subsets.
This approach mitigates the risk of overfitting by testing the model’s
performance on unseen data, yielding a robust estimate of prediction
error. The model’s effectiveness is quantified using the Mean Squared
Error (MSE), which measures the average squared difference between
observed and predicted cholesterol values. By employing
cross-validation, we ensure that the full model is evaluated rigorously,
providing insights into its accuracy and reliability in real-world
applications.
# Set up 10-fold cross-validation
train_control <- trainControl(method = "cv", number = 10) # 10-fold CV
# Fit the full model
full_model <- train(cholestrol ~ age + race + log_BMI + height + log_body.weight +
hbp + avg.dbp + log_avg.sbp + obese,
data = imputed_nhanes,
method = "lm",
trControl = train_control)
# Extract coefficients from the final model
coeff_table <- summary(full_model$finalModel)$coefficients %>%
as.data.frame() %>%
rownames_to_column(var = "Predictor") # Convert row names to a column
# Calculate Cross-Validation MSE
mse <- mean(full_model$resample$RMSE^2)
# Calculate R² from the final model
r2 <- summary(full_model$finalModel)$r.squared
# Create a data frame for MSE and R²
metrics_df <- data.frame(
Statistic = c("Mean Squared Error", "R-squared"),
Value = c(mse, r2)
)
# Print the coefficients using kable
kable(coeff_table, digits = 4, caption = "Regression Model Coefficients", format = "markdown")
Regression Model Coefficients
| (Intercept) |
-4.5758 |
1.7615 |
-2.5977 |
0.0094 |
| age |
0.2434 |
0.0134 |
18.1921 |
0.0000 |
| raceBlack |
-0.0679 |
0.0243 |
-2.7969 |
0.0052 |
| raceOther |
-0.2054 |
0.0671 |
-3.0602 |
0.0022 |
| log_BMI |
0.6340 |
0.8695 |
0.7292 |
0.4659 |
| height |
-0.0660 |
0.0975 |
-0.6772 |
0.4983 |
| log_body.weight |
0.1011 |
0.8701 |
0.1162 |
0.9075 |
| hbp |
-0.0668 |
0.0403 |
-1.6602 |
0.0969 |
| avg.dbp |
0.1091 |
0.0134 |
8.1549 |
0.0000 |
| log_avg.sbp |
0.4214 |
0.1375 |
3.0636 |
0.0022 |
| obese |
-0.0581 |
0.0367 |
-1.5830 |
0.1135 |
# Print MSE and R² using kable
kable(metrics_df, digits = 4, caption = "Model Performance Metrics (MSE & R²)", format = "markdown")
Model Performance Metrics (MSE & R²)
| Mean Squared Error |
0.8762 |
| R-squared |
0.1266 |
Stepwise BIC
In this analysis, a Stepwise BIC regression model is implemented to
identify the most parsimonious subset of predictors for estimating
cholesterol levels. The Stepwise BIC approach selects variables by
minimizing the Bayesian Information Criterion (BIC), which penalizes
model complexity to prevent overfitting. To assess the model’s
generalizability, we employ cross-validation by splitting the dataset
into training and testing subsets. The model is trained on the training
data, and predictions are made on the test data to compute the Mean
Squared Error (MSE) as a measure of model performance. Additionally, the
estimated regression coefficients in a structured table for clarity and
interpretation. This approach ensures an optimal balance between model
complexity and predictive accuracy while validating the model’s
robustness.
stepwise_bic_model <- function(train_data, test_data) {
# Fit initial full model
full_model2 <- lm(cholestrol ~ age + height + log_body.weight + avg.dbp, data = train_data)
# Perform stepwise selection using BIC
model_stepwise_BIC <- stepAIC(full_model2, direction = "both", k = log(nrow(train_data)), trace = FALSE)
# Extract coefficients
coeff_table <- summary(model_stepwise_BIC)$coefficients %>%
as.data.frame() %>%
rownames_to_column(var = "Predictor") # Convert row names to a column
# Print coefficients using kable
print(kable(coeff_table, digits = 4, caption = "Stepwise BIC Model Coefficients", format = "markdown"))
# Predict on test data
predictions <- predict(model_stepwise_BIC, newdata = test_data)
# Compute Mean Squared Error (MSE)
mse <- mean((test_data$cholestrol - predictions)^2)
# Compute R²
rss <- sum((test_data$cholestrol - predictions)^2) # Residual Sum of Squares
tss <- sum((test_data$cholestrol - mean(test_data$cholestrol))^2) # Total Sum of Squares
r2 <- 1 - rss/tss # R-squared
# Create a data frame for MSE and R²
metrics_df <- data.frame(
Statistic = c("Mean Squared Error", "R-squared"),
Value = c(mse, r2)
)
# Print MSE and R² using kable
print(kable(metrics_df, digits = 4, caption = "Stepwise BIC Model Performance Metrics (MSE & R²)", format = "markdown"))
return(list(model = model_stepwise_BIC, mse = mse, r2 = r2)) # Return the model, MSE, and R²
}
# Example usage (Assuming you have a dataset split into train_data and test_data)
set.seed(123)
train_index <- createDataPartition(imputed_nhanes$cholestrol, p = 0.8, list = FALSE)
train_data <- imputed_nhanes[train_index, ]
test_data <- imputed_nhanes[-train_index, ]
# Run the function
stepwise_results <- stepwise_bic_model(train_data, test_data)
Table: Stepwise BIC Model Coefficients
|Predictor | Estimate| Std. Error| t value| Pr(>|t|)|
|:---------------|--------:|----------:|-------:|------------------:|
|(Intercept) | -3.3622| 0.3416| -9.8427| 0|
|age | 0.2671| 0.0120| 22.2994| 0|
|height | -0.1274| 0.0138| -9.2161| 0|
|log_body.weight | 0.6589| 0.0669| 9.8520| 0|
|avg.dbp | 0.1191| 0.0125| 9.5632| 0|
Table: Stepwise BIC Model Performance Metrics (MSE & R²)
|Statistic | Value|
|:------------------|------:|
|Mean Squared Error | 0.8383|
|R-squared | 0.1425|
Ridge Regression
Ridge regression is applied to the NHANES dataset to address
multicollinearity among predictor variables while maintaining model
stability. By imposing a penalty on large coefficients through L2
regularization, Ridge regression prevents overfitting and improves
generalization. The model was evaluated using cross-validation, and its
performance was compared to other regression approaches based on Mean
Squared Error (MSE) and R².
# Define predictor matrix (X) and response variable (y)
X <- as.matrix(imputed_nhanes[, c("age", "log_BMI", "height", "log_body.weight", "avg.dbp", "log_avg.sbp")])
y <- imputed_nhanes$cholestrol
set.seed(123) # For reproducibility
# Define training control with 10-fold cross-validation
train_control <- trainControl(method = "cv", number = 10)
# Train Ridge Regression Model
ridge_model <- train(
x = X, y = y,
method = "glmnet",
trControl = train_control,
tuneGrid = expand.grid(alpha = 0, lambda = 10^seq(-3, 3, length = 100)) # Ridge: alpha = 0
)
# Best lambda value selected via cross-validation
best_lambda <- ridge_model$bestTune$lambda
# Predict using best lambda
ridge_predictions <- predict(ridge_model, X)
# Compute Mean Squared Error (MSE)
ridge_mse <- mean((y - ridge_predictions)^2)
# Compute R²
rss <- sum((y - ridge_predictions)^2) # Residual Sum of Squares
tss <- sum((y - mean(y))^2) # Total Sum of Squares
ridge_r2 <- 1 - (rss / tss) # R² calculation
# Extract coefficients from the final Ridge model
coefficients <- as.data.frame(as.matrix(ridge_model$finalModel$beta)) %>%
tibble::rownames_to_column(var = "Predictor") # Convert row names to a column
#print(kable(head(coefficients, 10), digits = 4, caption = "Top 10 Ridge Regression Coefficients", format = "markdown"))
# Create a data frame for MSE, R², and Best Lambda
metrics_df <- data.frame(
Statistic = c("Mean Squared Error", "R-squared", "Optimal Lambda"),
Value = c(ridge_mse, ridge_r2, best_lambda)
)
# Print model performance metrics using kable()
print(kable(metrics_df, digits = 4, caption = "Ridge Regression Performance Metrics", format = "markdown"))
Table: Ridge Regression Performance Metrics
|Statistic | Value|
|:------------------|------:|
|Mean Squared Error | 0.8757|
|R-squared | 0.1242|
|Optimal Lambda | 0.0285|
Comparison
After performing cross-validation on the NHANES_imputed dataset, the
predictive performance of the thre models are compared. The BIC model
has the smallest MSE and highest R^2 value, indicating a better
performing regression model. In, turn, the BIC model is recommended.
LOGISTIC
REGRESSION
This analysis applies logistic regression to predict current smoking
status using data from NHANES. Four models are developed: a full model
including all relevant predictors, a stepwise AIC model for variable
selection, a BIC model for a more parsimonious approach, and a
regularized logistic regression model using Lasso to prevent
overfitting. The models are evaluated and compared using Receiver
Operating Characteristic (ROC) curves and the Area Under the Curve (AUC)
to assess their classification performance and predictive accuracy.
Full Model
The first candidate, a full model, is fitted to predict current
smoking status.
# Fit logistic regression model
full_model <- glm(current.smk ~ age + race + log_body.weight + log_BMI + obese +
height + log_avg.sbp + avg.dbp + log_cholestrol + hbp,
data = imputed_nhanes,
family = binomial)
# Extract coefficients from the model
coefficients <- summary(full_model)$coefficients
# Convert coefficients to a data frame and add row names as a column
coeff_table <- as.data.frame(coefficients) %>%
tibble::rownames_to_column(var = "Predictor") # Convert row names to a column
# Print the coefficients table using kable
kable(coeff_table, digits = 4, caption = "Logistic Regression Model Coefficients", format = "markdown")
Logistic Regression Model Coefficients
| (Intercept) |
-9.6100 |
4.2576 |
-2.2571 |
0.0240 |
| age |
0.8518 |
0.0341 |
24.9430 |
0.0000 |
| raceBlack |
-0.8473 |
0.0583 |
-14.5294 |
0.0000 |
| raceOther |
-0.3106 |
0.1567 |
-1.9825 |
0.0474 |
| log_body.weight |
2.6402 |
2.0813 |
1.2685 |
0.2046 |
| log_BMI |
-0.6406 |
2.0792 |
-0.3081 |
0.7580 |
| obese |
-0.1208 |
0.0867 |
-1.3930 |
0.1636 |
| height |
-0.2097 |
0.2330 |
-0.8999 |
0.3682 |
| log_avg.sbp |
-0.4720 |
0.3284 |
-1.4375 |
0.1506 |
| avg.dbp |
0.0655 |
0.0324 |
2.0189 |
0.0435 |
| log_cholestrol |
0.1397 |
0.1348 |
1.0365 |
0.3000 |
| hbp |
-0.0763 |
0.0957 |
-0.7975 |
0.4252 |
Age, race, and average dbp are significant features in the full
model.
Stepwise AIC
Model
The second model, stepwise AIC, is fitted to predict current smoking
status. This model automatically selects a subset of predictors using
AIC-based stepwise selection.
# Fit initial full model
full_model <- glm(current.smk ~ age + race + log_body.weight + log_BMI + obese +
height + log_avg.sbp + avg.dbp + log_cholestrol + hbp,
data = imputed_nhanes,
family = binomial)
# Perform stepwise selection using AIC
stepwise_model <- stepAIC(full_model, direction = "both", trace = FALSE)
# Extract coefficients from the stepwise model
coefficients_stepwise <- summary(stepwise_model)$coefficients
# Convert coefficients to a data frame and add row names as a column
coeff_table_stepwise <- as.data.frame(coefficients_stepwise) %>%
tibble::rownames_to_column(var = "Predictor") # Convert row names to a column
# Print the coefficients table using kable
kable(coeff_table_stepwise, digits = 4, caption = "Stepwise Logistic Regression Model Coefficients", format = "markdown")
Stepwise Logistic Regression Model Coefficients
| (Intercept) |
-7.1104 |
1.5749 |
-4.5148 |
0.0000 |
| age |
0.8583 |
0.0335 |
25.6466 |
0.0000 |
| raceBlack |
-0.8515 |
0.0582 |
-14.6232 |
0.0000 |
| raceOther |
-0.3164 |
0.1565 |
-2.0212 |
0.0433 |
| log_body.weight |
2.0364 |
0.2072 |
9.8304 |
0.0000 |
| obese |
-0.1260 |
0.0865 |
-1.4575 |
0.1450 |
| height |
-0.1417 |
0.0341 |
-4.1583 |
0.0000 |
| log_avg.sbp |
-0.6344 |
0.2525 |
-2.5125 |
0.0120 |
| avg.dbp |
0.0698 |
0.0323 |
2.1632 |
0.0305 |
All features, other than obese, are significant in the stepwise AIC
model.
BIC Model
The third candidate,a BIC model, is fitted to predict current smoking
status. This model fits a more parsimonious model.
# Fit initial full model
full_model <- glm(current.smk ~ age + race + log_body.weight + log_BMI + obese +
height + log_avg.sbp + avg.dbp + log_cholestrol + hbp,
data = imputed_nhanes,
family = binomial)
# Perform stepwise selection using BIC (using k = log(nrow(imputed_nhanes)))
bic_model <- stepAIC(full_model, direction = "both", k = log(nrow(imputed_nhanes)), trace = FALSE)
# Extract coefficients from the BIC model
coefficients_bic <- summary(bic_model)$coefficients
# Convert coefficients to a data frame and add row names as a column
coeff_table_bic <- as.data.frame(coefficients_bic) %>%
tibble::rownames_to_column(var = "Predictor") # Convert row names to a column
# Print the coefficients table using kable
kable(coeff_table_bic, digits = 4, caption = "BIC Stepwise Model Coefficients", format = "markdown")
BIC Stepwise Model Coefficients
| (Intercept) |
-9.1460 |
0.7115 |
-12.8541 |
0.0000 |
| age |
0.8140 |
0.0268 |
30.3780 |
0.0000 |
| raceBlack |
-0.8552 |
0.0579 |
-14.7775 |
0.0000 |
| raceOther |
-0.3123 |
0.1562 |
-1.9988 |
0.0456 |
| log_body.weight |
1.8297 |
0.1394 |
13.1290 |
0.0000 |
| height |
-0.1092 |
0.0290 |
-3.7669 |
0.0002 |
All features are significant in the BIC model.
Regularized Logistic
Regression Model (Lasso)
The fourth candidate model uses Regularized Logistic Regression
(Lasso). This method selects the most important predictors.
# Define predictor matrix (X) and response variable (y)
x <- model.matrix(current.smk ~ age + race + log_body.weight + log_BMI + obese + height + log_avg.sbp + avg.dbp + log_cholestrol + hbp,
data = imputed_nhanes)[, -1] # Exclude intercept
y <- imputed_nhanes$current.smk
# Train the Lasso model using cross-validation
lasso_model <- cv.glmnet(x, y, family = "binomial", alpha = 1)
# Best lambda value selected via cross-validation
best_lambda_lasso <- lasso_model$lambda.min
print(paste("Optimal Lambda for Lasso:", best_lambda_lasso))
[1] "Optimal Lambda for Lasso: 0.000783042982863974"
# Extract coefficients from the final Lasso model at the optimal lambda
lasso_coefficients <- coef(lasso_model, s = "lambda.min")
# Convert coefficients to a data frame
lasso_coefficients_df <- as.data.frame(as.matrix(lasso_coefficients))
lasso_coefficients_df$Predictor <- rownames(lasso_coefficients_df)
# Reorder columns to make predictor names the first column
lasso_coefficients_df <- lasso_coefficients_df %>%
select(Predictor, everything())
# Print the coefficients table using kable
kable(lasso_coefficients_df, digits = 4, caption = "Lasso Regression Coefficients", format = "markdown")
Lasso Regression Coefficients
| (Intercept) |
(Intercept) |
-6.4108 |
| age |
age |
0.8393 |
| raceBlack |
raceBlack |
-0.8379 |
| raceOther |
raceOther |
-0.2843 |
| log_body.weight |
log_body.weight |
0.7797 |
| log_BMI |
log_BMI |
1.1279 |
| obese |
obese |
-0.0866 |
| height |
height |
0.0000 |
| log_avg.sbp |
log_avg.sbp |
-0.3598 |
| avg.dbp |
avg.dbp |
0.0546 |
| log_cholestrol |
log_cholestrol |
0.1316 |
| hbp |
hbp |
-0.0746 |
Comparison
In this analysis, four logistic regression models—Full Model,
Stepwise AIC Model, BIC Model, and Lasso Model are compared using ROC
(Receiver Operating Characteristic) curves and AUC (Area Under the
Curve) to assess their predictive performance. The optimal cutoff for
each model using Youden’s Index is determined, which maximizes the
balance between sensitivity and specificity. By evaluating these models
through both graphical and numerical metrics, insights is gained into
which model provides the best classification performance for predicting
the outcome variable.
# Full Model
full_model <- glm(current.smk ~ age + race + log_body.weight + log_BMI + obese + height + log_avg.sbp + avg.dbp + log_cholestrol + hbp, data = imputed_nhanes, family = binomial)
# Stepwise AIC Model
stepwise_model <- stepAIC(full_model, direction = "both", trace = FALSE)
# BIC Model
bic_model <- stepAIC(full_model, direction = "both", k = log(nrow(imputed_nhanes)), trace = FALSE)
# Lasso Model
x <- model.matrix(current.smk ~ age + race + log_body.weight + log_BMI + obese + height + log_avg.sbp + avg.dbp + log_cholestrol + hbp , data = imputed_nhanes)[, -1] # Exclude intercept
y <- imputed_nhanes$current.smk
lasso_cv <- cv.glmnet(x, y, family = "binomial", alpha = 1)
lasso_model <- glmnet(x, y, family = "binomial", alpha = 1, lambda = lasso_cv$lambda.min)
# Get predicted probabilities
imputed_nhanes$full_pred <- predict(full_model, type = "response")
imputed_nhanes$stepwise_pred <- predict(stepwise_model, type = "response")
imputed_nhanes$bic_pred <- predict(bic_model, type = "response")
imputed_nhanes$lasso_pred <- predict(lasso_model, newx = x, type = "response")[,1]
# Create ROC curves
full_roc <- roc(imputed_nhanes$current.smk, imputed_nhanes$full_pred)
stepwise_roc <- roc(imputed_nhanes$current.smk, imputed_nhanes$stepwise_pred)
bic_roc <- roc(imputed_nhanes$current.smk, imputed_nhanes$bic_pred)
lasso_roc <- roc(imputed_nhanes$current.smk, imputed_nhanes$lasso_pred)
# Calculate optimal cutoff using Youden's Index
optimal_cutoff_full <- full_roc$specificities[which.max(full_roc$sensitivities + full_roc$specificities - 1)]
optimal_cutoff_stepwise <- stepwise_roc$specificities[which.max(stepwise_roc$sensitivities + stepwise_roc$specificities - 1)]
optimal_cutoff_bic <- bic_roc$specificities[which.max(bic_roc$sensitivities + bic_roc$specificities - 1)]
optimal_cutoff_lasso <- lasso_roc$specificities[which.max(lasso_roc$sensitivities + lasso_roc$specificities - 1)]
# Print optimal cutoffs
cat("Optimal cutoff for Full Model:", optimal_cutoff_full, "\n")
Optimal cutoff for Full Model: 0.714215
cat("Optimal cutoff for Stepwise AIC Model:", optimal_cutoff_stepwise, "\n")
Optimal cutoff for Stepwise AIC Model: 0.7191679
cat("Optimal cutoff for BIC Model:", optimal_cutoff_bic, "\n")
Optimal cutoff for BIC Model: 0.7209014
cat("Optimal cutoff for Lasso Model:", optimal_cutoff_lasso, "\n")
Optimal cutoff for Lasso Model: 0.7013373
# Plot all ROC curves
ggplot() +
geom_line(aes(x = full_roc$specificities, y = full_roc$sensitivities, color = "Full Model")) +
geom_line(aes(x = stepwise_roc$specificities, y = stepwise_roc$sensitivities, color = "Stepwise AIC")) +
geom_line(aes(x = bic_roc$specificities, y = bic_roc$sensitivities, color = "BIC Model")) +
geom_line(aes(x = lasso_roc$specificities, y = lasso_roc$sensitivities, color = "Lasso Model")) +
labs(title = "ROC Curve Comparison", x = "1 - Specificity", y = "Sensitivity") +
scale_color_manual(values = c("red", "blue", "green", "purple", "orange")) +
theme_minimal()

# AUC values
auc_values <- data.frame(
Model = c("Full Model", "Stepwise AIC", "BIC Model", "Lasso Model"),
AUC = c(auc(full_roc), auc(stepwise_roc), auc(bic_roc), auc(lasso_roc))
)
print(auc_values)
Model AUC
1 Full Model 0.7509937
2 Stepwise AIC 0.7507281
3 BIC Model 0.7496650
4 Lasso Model 0.7508550
All models have very similar AUC. In turn, the more parsimonious
model, BIC, is recommended.
LS0tCnRpdGxlOiAnQW5hbHlzaXMgb2YgVGhlIE5hdGlvbmFsIEhlYWx0aCBhbmQgTnV0cml0aW9uIEV4YW1pbmF0aW9uIFN1cnZleSAoTkhBTkVTKScKYXV0aG9yOiAiSm9hbm5lIE11c2EiCmRhdGU6ICJNYXJjaCA1LCAyMDI1IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICBmaWdfd2lkdGg6IDMKICAgIGZpZ19oZWlnaHQ6IDMKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7PWh0bWx9Cgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKLyogQ2FzY2FkaW5nIFN0eWxlIFNoZWV0cyAoQ1NTKSBpcyBhIHN0eWxlc2hlZXQgbGFuZ3VhZ2UgdXNlZCB0byBkZXNjcmliZSB0aGUgcHJlc2VudGF0aW9uIG9mIGEgZG9jdW1lbnQgd3JpdHRlbiBpbiBIVE1MIG9yIFhNTC4gaXQgaXMgYSBzaW1wbGUgbWVjaGFuaXNtIGZvciBhZGRpbmcgc3R5bGUgKGUuZy4sIGZvbnRzLCBjb2xvcnMsIHNwYWNpbmcpIHRvIFdlYiBkb2N1bWVudHMuICovCgpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLwogIGZvbnQtc2l6ZTogMjRweDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9Cmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8KICBmb250LXNpemU6IDIwcHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7CiAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7CiAgY29sb3I6IERhcmtCbHVlOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAyMnB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKICAgIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMTZweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CgppZiAoIXJlcXVpcmUoIk1BU1MiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikKICAgbGlicmFyeShNQVNTKQp9CmlmICghcmVxdWlyZSgibGVhZmxldCIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKQogICBsaWJyYXJ5KGxlYWZsZXQpCn0KCmlmICghcmVxdWlyZSgiZ2dyaWRnZXMiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpCmxpYnJhcnkoZ2dyaWRnZXMpCn0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKfQoKaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCiAgIGxpYnJhcnkoZ2dwbG90MikKfQppZiAoIXJlcXVpcmUoIm5uZXQiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJubmV0IikKICAgbGlicmFyeShubmV0KQp9CiAKaWYgKCFyZXF1aXJlKCJub3J0ZXN0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygibm9ydGVzdCIpCiAgIGxpYnJhcnkobm9ydGVzdCkKfSAKCmlmICghcmVxdWlyZSgiY29ycnBsb3QiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIpCiAgIGxpYnJhcnkoY29ycnBsb3QpCn0KCmlmICghcmVxdWlyZSgiVklNIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiVklNIikKICAgbGlicmFyeShWSU0pCn0KCgppZiAoIXJlcXVpcmUoIm1pY2UiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJtaWNlIikKICAgbGlicmFyeShtaWNlKQp9CgppZiAoIXJlcXVpcmUoIm5hbmlhciIpKSB7CiAgIGluc3RhbGwucGFja2FnZXMoIm5hbmlhciIpCiAgIGxpYnJhcnkobmFuaWFyKQoKfQoKaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQogICBsaWJyYXJ5KEdHYWxseSkKCn0KCmlmICghcmVxdWlyZSgiZ2xtbmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IikKICAgbGlicmFyeShnbG1uZXQpCgp9CgppZiAoIXJlcXVpcmUoImNhcmV0IikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQogICBsaWJyYXJ5KGNhcmV0KQoKfQoKaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygicFJPQyIpCiAgIGxpYnJhcnkocFJPQykKCn0KCgprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgICAgICAgICAgICAgICAgICAgICBlY2hvID0gVFJVRSwgICAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gVFJVRSwgICAgCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEKICAgICAgICAgICAgICAgICAgICAgICkgIApgYGAKCgoKIyBJTlRST0RVQ1RJT04KCgpgYGB7cn0KbmhhbmVzIDwtIHJlYWQuY3N2KCJodHRwczovL2ptdXNhMy5naXRodWIuaW8vam11c2EvbmhhbmVzLmNzdiIpCgojQ1JFQVRFIE1JU1NJTkcgVkFMVUVTIGluIHNvbWUgdmFyaWFibGVzCgojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKSAgCgojIERldGVybWluZSB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzICgxNSUgb2YgdG90YWwgcm93cykKbnVtX21pc3NpbmcgPC0gcm91bmQoMC4xNCAqIG5yb3cobmhhbmVzKSkKCiMgUmFuZG9tbHkgc2VsZWN0IHJvdyBpbmRpY2VzIHRvIGludHJvZHVjZSBtaXNzaW5nIHZhbHVlcyAKbWlzc2luZ19pbmRpY2VzIDwtIHNhbXBsZSgxOm5yb3cobmhhbmVzKSwgbnVtX21pc3NpbmcsIHJlcGxhY2UgPSBGQUxTRSkKCiMgU2V0IHNlbGVjdGVkIHJvd3MgaW4gYm9keS53ZWlnaHQgdG8gTkEKbmhhbmVzJGJvZHkud2VpZ2h0W21pc3NpbmdfaW5kaWNlc10gPC0gTkEKCiMgVmVyaWZ5IG1pc3NpbmcgdmFsdWVzCiNzdW0oaXMubmEobmhhbmVzJGJvZHkud2VpZ2h0KSkgIAoKCiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCg0NTYpICAKCiMgRGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgKDE1JSBvZiB0b3RhbCByb3dzKQpudW1fbWlzc2luZyA8LSByb3VuZCgwLjE1ICogbnJvdyhuaGFuZXMpKQoKIyBSYW5kb21seSBzZWxlY3Qgcm93IGluZGljZXMgdG8gaW50cm9kdWNlIG1pc3NpbmcgdmFsdWVzCm1pc3NpbmdfaW5kaWNlcyA8LSBzYW1wbGUoMTpucm93KG5oYW5lcyksIG51bV9taXNzaW5nLCByZXBsYWNlID0gRkFMU0UpCgojIFNldCBzZWxlY3RlZCByb3dzIGluIGJvZHkud2VpZ2h0IHRvIE5BCm5oYW5lcyRjaG9sZXN0cm9sW21pc3NpbmdfaW5kaWNlc10gPC0gTkEKCgojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoNzg5KSAgCgojIERldGVybWluZSB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzICgxNSUgb2YgdG90YWwgcm93cykKbnVtX21pc3NpbmcgPC0gcm91bmQoMC4xNSAqIG5yb3cobmhhbmVzKSkKCiMgUmFuZG9tbHkgc2VsZWN0IHJvdyBpbmRpY2VzIHRvIGludHJvZHVjZSBtaXNzaW5nIHZhbHVlcwptaXNzaW5nX2luZGljZXMgPC0gc2FtcGxlKDE6bnJvdyhuaGFuZXMpLCBudW1fbWlzc2luZywgcmVwbGFjZSA9IEZBTFNFKQoKIyBTZXQgc2VsZWN0ZWQgcm93cyBpbiByYWNlIHRvIE5BCm5oYW5lcyRyYWNlW21pc3NpbmdfaW5kaWNlc10gPC0gTkEKCiMgVmVyaWZ5IG1pc3NpbmcgdmFsdWVzCgojc3VtKGlzLm5hKG5oYW5lcyRyYWNlKSkgIAojc3VtKGlzLm5hKG5oYW5lcyRjaG9sZXN0cm9sKSkgCiNzdW0oaXMubmEobmhhbmVzJGJvZHkud2VpZ2h0KSkgCgojIGNyZWF0ZSBhIGNvcHkgb2YgbmhhbmVzIGZvciBNSUNFCm5oYW5lczEgPC0gbmhhbmVzCm5oYW5lczEkcmFjZSA8LSBhcy5mYWN0b3IobmhhbmVzMSRyYWNlKQpuaGFuZXMxJGNob2xlc3Ryb2wgPC0gYXMubnVtZXJpYyhuaGFuZXMxJGNob2xlc3Ryb2wpCgoKCmBgYAoKClRoZSBOYXRpb25hbCBIZWFsdGggYW5kIE51dHJpdGlvbiBFeGFtaW5hdGlvbiBTdXJ2ZXkgKE5IQU5FUykgaXMgYSBwcm9ncmFtIGNvbmR1Y3RlZCBieSB0aGUgTmF0aW9uYWwgQ2VudGVyIGZvciBIZWFsdGggU3RhdGlzdGljcyAoTkNIUyksIGEgZGl2aXNpb24gb2YgdGhlIENlbnRlcnMgZm9yIERpc2Vhc2UgQ29udHJvbCBhbmQgUHJldmVudGlvbiAoQ0RDKS4gTkhBTkVTIGhhcyBiZWVuIGNvbmR1Y3RlZCBpbiB2YXJpb3VzIGZvcm1zIHNpbmNlIHRoZSBlYXJseSAxOTYwcywgd2l0aCB0aGUgY3VycmVudCBjb250aW51b3VzIHN1cnZleSBmb3JtYXQgYmVnaW5uaW5nIGluIDE5OTkuIFRoZSBzdXJ2ZXkgaXMgZGVzaWduZWQgdG8gYXNzZXNzIHRoZSBoZWFsdGggYW5kIG51dHJpdGlvbmFsIHN0YXR1cyBvZiBhZHVsdHMgYW5kIGNoaWxkcmVuIGluIHRoZSBVbml0ZWQgU3RhdGVzIHRocm91Z2ggYSBjb21iaW5hdGlvbiBvZiBpbnRlcnZpZXdzLCBwaHlzaWNhbCBleGFtaW5hdGlvbnMsIGFuZCBsYWJvcmF0b3J5IHRlc3RzLiBEYXRhIGlzIGNvbGxlY3RlZCBmcm9tIGEgbmF0aW9uYWxseSByZXByZXNlbnRhdGl2ZSBzYW1wbGUgb2YgdGhlIFUuUy4gcG9wdWxhdGlvbiB1c2luZyBhIGNvbXBsZXgsIG11bHRpc3RhZ2UgcHJvYmFiaWxpdHkgc2FtcGxpbmcgZGVzaWduLiBQYXJ0aWNpcGFudHMgdW5kZXJnbyBob3VzZWhvbGQgaW50ZXJ2aWV3cyBmb2xsb3dlZCBieSBoZWFsdGggZXhhbWluYXRpb25zIGF0IG1vYmlsZSBleGFtaW5hdGlvbiBjZW50ZXJzIChNRUNzKSwgd2hlcmUgdHJhaW5lZCBtZWRpY2FsIHByb2Zlc3Npb25hbHMgY29sbGVjdCBiaW9sb2dpY2FsIHNhbXBsZXMgYW5kIGNvbmR1Y3QgY2xpbmljYWwgYXNzZXNzbWVudHMuIFRoZSBOSEFORVMgZGF0YSBwcm92aWRlcyBjcml0aWNhbCBpbnNpZ2h0cyBpbnRvIHB1YmxpYyBoZWFsdGggdHJlbmRzLCBoZWxwaW5nIHBvbGljeW1ha2VycywgcmVzZWFyY2hlcnMsIGFuZCBoZWFsdGhjYXJlIHByb2Zlc3Npb25hbHMgbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgb24gbmF0aW9uYWwgaGVhbHRoIHByaW9yaXRpZXMuIFRoZSBwdXJwb3NlIG9mIHRoaXMgc3R1ZHkgaXMgdG8gYXNzZXNzIHRoZSBoZWFsdGggYW5kIG51dHJpdGlvbmFsIHN0YXR1cyBvZiB0aGUgc3R1ZHkgcGFydGljaXBhbnRzIHdpdGggbWV0cmljcyBzdWNoIGFzIGhpZ2ggYmxvb2QgcHJlc3N1cmUuIAoKVGhlcmUgYXJlIDE1IGZlYXR1cmVzIGFuZCA3OTI3IG9ic2VydmF0aW9ucyBpbiB0aGlzIGRhdGEgc2V0LiBUaGVzZSBmZWF0dXJlcyBhcmUgbGlzdGVkIGJlbG93LgoKMSkgb2JzOiBSZXNwb25kZW50IElkZW50aWZpY2F0aW9uIE51bWJlcgoyKSBwc3U6IFBzZXVkby1QU1UgMSwyIHBzdQozKSBzdHJhdHVtOiBQc2V1ZG8tc3RyYXR1bSAoMDEgLSA0OSkKNCkgc3RhdC53ZWlnaHQ6IFN0YXRpc3RpY2FsIHdlaWdodCAoMjI1LjkzIC0gMTM5NzQ0LjkpCjUpIGFnZTogQWdlICh5ZWFycykgCjYpIHNleDogMCA9IEZlbWFsZSwgMSA9IE1hbGUgc2V4CjcpIHJhY2U6IFJhY2UgKDEgPSBXaGl0ZTsgMiA9IEJsYWNrOyAzID0gT3RoZXIpCjgpIGJvZHkud2VpZ2h0OiBCb2R5IFdlaWdodCAocG91bmRzKQo5KSBoZWlnaHQ6IFN0YW5kaW5nIEhlaWdodCAoaW5jaGVzKQoxMCkgYXZnLnNicDogQXZlcmFnZSBTeXN0b2xpYyBCUCAobW0vSGcpIAoxMSkgYXZnLmRicDogQXZlcmFnZSBEaWFzdG9saWMgQlAgKG1tL0hnKSAKMTIpIHBhc3Quc21rOiBIYXMgcmVzcG9uZGVudCBzbW9rZWQgPiAxMDAgY2lnYXJldHRlcyBpbiBsaWZlICgxID0gWWVzLCAyID0gTm8pCjEzKSBjdXJyZW50LnNtazogRG9lcyByZXNwb25kZW50IHNtb2tlIGNpZ2FyZXR0ZXMgbm93PyAoMSA9IFllcywgMiA9IE5vKQoxNCkgY2hvbGVzdHJvbDogU2VydW0gQ2hvbGVzdGVyb2wgKG1nLzEwMG1sKSAKMTUpIGhicDogSGlnaCBCbG9vZCBQcmVzc3VyZSAoMCBpZiBQRVBNTksxUiA8PSAxNDA7IDEgaWYgUEVQTU5LMVIgPiAxNDApCgpUaGUgb2JzLCBwc3UsIHN0cmF0dW0sIGFuZCBzdGF0LndlaWdodCB2YXJpYWJsZXMgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGlzIGFuYWx5c2lzIGJlY2F1c2UgdGhleSBkbyBub3QgZGlyZWN0bHkgcHJvdmlkZSBoZWFsdGgtcmVsYXRlZCBpbmZvcm1hdGlvbi4gCgpUaGUgZGF0YSBzZXQgaGFzIDMxNyBtaXNzaW5nIHZhbHVlcyBmb3IgYm9keSB3ZWlnaHQgYW5kIDM5NiBtaXNzaW5nIHZhbHVlcyBmb3IgY2hvbGVzdGVyb2wuIAoKIyBFREEKCgojIyBEaXN0cmlidXRpb24gb2YgSW5kaXZpZHVhbCBGZWF0dXJlcwoKVGhlIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIGluZGl2aWR1YWwgZmVhdHVyZXMgaW4gdGhlIE5IQU5FUyBkYXRhc2V0IHByb3ZpZGUgYSBzbmFwc2hvdCBvZiB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBwb3B1bGF0aW9uIGluIHRlcm1zIG9mIHZhcmlvdXMgaGVhbHRoIGFuZCBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMuIFVuZGVyc3RhbmRpbmcgdGhlc2UgZGlzdHJpYnV0aW9ucyBpcyBlc3NlbnRpYWwgZm9yIGlkZW50aWZ5aW5nIHBhdHRlcm5zLCBkZXRlY3RpbmcgcG90ZW50aWFsIGJpYXNlcywgYW5kIGFzc2Vzc2luZyB0aGUgcmFuZ2UgYW5kIHZhcmlhYmlsaXR5IG9mIGVhY2ggZmVhdHVyZS4gVGhpcyBhbmFseXNpcyBoZWxwcyBpbiB1bmRlcnN0YW5kaW5nIGhvdyB2YXJpYWJsZXMgc3VjaCBhcyBhZ2UsIGJvZHkgd2VpZ2h0LCBibG9vZCBwcmVzc3VyZSwgYW5kIGxpZmVzdHlsZSBmYWN0b3JzIGFyZSBzcHJlYWQgYWNyb3NzIHRoZSBkYXRhc2V0LCB3aGljaCBpcyBjcnVjaWFsIGZvciBzdWJzZXF1ZW50IHN0YXRpc3RpY2FsIG1vZGVsaW5nIGFuZCBpbnRlcnByZXRhdGlvbi4KCmBgYHtyfQoKIyBDb252ZXJ0IHZhcmlhYmxlcyB0byBmYWN0b3JzIHdpdGggcHJvcGVyIGxhYmVscwpuaGFuZXMkc2V4IDwtIGZhY3RvcihuaGFuZXMkc2V4LCBsZXZlbHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBjKCJGZW1hbGUiLCAiTWFsZSIpKQpuaGFuZXMkcmFjZSA8LSBmYWN0b3IobmhhbmVzJHJhY2UsIGxldmVscyA9IGMoMSwgMiwgMyksIGxhYmVscyA9IGMoIldoaXRlIiwgIkJsYWNrIiwgIk90aGVyIikpCm5oYW5lcyRwYXN0LnNtayA8LSBmYWN0b3IobmhhbmVzJHBhc3Quc21rLCBsZXZlbHMgPSBjKDEsIDIpLCBsYWJlbHMgPSBjKCJZZXMiLCAiTm8iKSkKbmhhbmVzJGN1cnJlbnQuc21rIDwtIGZhY3RvcihuaGFuZXMkY3VycmVudC5zbWssIGxldmVscyA9IGMoMSwgMiksIGxhYmVscyA9IGMoIlllcyIsICJObyIpKQoKIyBDcmVhdGUgZnJlcXVlbmN5IHRhYmxlcwpzZXhfY291bnQgPC0gdGFibGUobmhhbmVzJHNleCkKCnJhY2VfY291bnQgPC0gdGFibGUobmhhbmVzJHJhY2UpCgpwYXN0LnNta19jb3VudCA8LSB0YWJsZShuaGFuZXMkcGFzdC5zbWspCgpjdXJyZW50LnNta19jb3VudCA8LSB0YWJsZShuaGFuZXMkY3VycmVudC5zbWspCgoKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgUGxvdCB0aGUgYmFyIGNoYXJ0cwpiYXJwbG90KGhlaWdodCA9IHNleF9jb3VudCwgY29sID0gInN0ZWVsYmx1ZSIsIG1haW4gPSAiU2V4IiwgeGxhYiA9ICJTZXgiLCB5bGFiID0gIkNvdW50IikKCmJhcnBsb3QoaGVpZ2h0ID0gcmFjZV9jb3VudCwgY29sID0gInN0ZWVsYmx1ZSIsIG1haW4gPSAiUmFjZSIsIHhsYWIgPSAiUmFjZSIsIHlsYWIgPSAiQ291bnQiKQoKYmFycGxvdChoZWlnaHQgPSBwYXN0LnNta19jb3VudCwgY29sID0gInN0ZWVsYmx1ZSIsIG1haW4gPSAiUGFzdCBTbW9rZXIiLCB4bGFiID0gIlBhc3QgU21va2VyIiwgeWxhYiA9ICJDb3VudCIpCgpiYXJwbG90KGhlaWdodCA9IGN1cnJlbnQuc21rX2NvdW50LCBjb2wgPSAic3RlZWxibHVlIiwgbWFpbiA9ICJDdXJyZW50IFNtb2tlciIsIHhsYWIgPSAiQ3VycmVudCBTbW9rZXIiLCB5bGFiID0gIkNvdW50IikKCmBgYAoKVGhlcmUgYXJlIHNsaWdodGx5IG1vcmUgbWFsZSBvYnNlcnZhdGlvbnMgdGhhbiBmZW1hbGUgb2JzZXJ2YXRpb24gaW4gdGhpcyBkYXRhIHNldC4gVGhlIG1ham9yaXR5IG9mIHBhcnRpY2lwYW50cyBhcmUgd2hpdGUsIGZvbGxvd2VkIGJ5IGJsYWNrLiBBbGwgcGFydGljaXBhbnRzIGFyZSByZWNvcmRlZCBhcyBiZWluZyBwYXN0IHNtb2tlcnMsIHdoaWNoIGlzIGxpa2VseSBhbiBlcnJvci4gVGhlcmUgaXMgYW4gaW1iYWxhbmNlIGZvciBwYXN0IHNtb2tlci4gSW4gdHVybiwgcGFzdCBzbW9rZXIgY2Fubm90IGJlIHVzZWQgZm9yIGFuYWx5c2lzLiBBYm91dCBoYWxmIG9mIGN1cnJlbnQgcGFydGljaXBhbnRzIGFyZSBjdXJyZW50IHNtb2tlcnMuIAoKYGBge3J9CgpwYXIobWZyb3cgPSBjKDIsIDIpKSAgCgojIEhpc3RvZ3JhbSBmb3IgQ2hvbGVzdGVyb2wKaGlzdChuaGFuZXMkY2hvbGVzdHJvbCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQ2hvbGVzdGVyb2wiLCAKICAgICB4bGFiID0gIkNob2xlc3Rlcm9sIiwgCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIGJvcmRlciA9ICJibGFjayIsIAogICAgIGJyZWFrcyA9IDIwKSAgIyBDb250cm9sIG51bWJlciBvZiBiaW5zCiAgICAKICAgICAKIyBIaXN0b2dyYW0gZm9yIFN5c3RvbGljIEJQCmhpc3QobmhhbmVzJGF2Zy5zYnAsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIFN5c3RvbGljIEJQIiwgCiAgICAgeGxhYiA9ICJTeXN0b2xpYyBCUCIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKICAgIAoKIyBIaXN0b2dyYW0gZm9yIEJvZHkgV2VpZ2h0Cmhpc3QobmhhbmVzJGJvZHkud2VpZ2h0LCAKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBCb2R5IFdlaWdodCIsIAogICAgIHhsYWIgPSAiQm9keSBXZWlnaHQiLCAKICAgICBjb2wgPSAic3RlZWxibHVlIiwgCiAgICAgYm9yZGVyID0gImJsYWNrIiwgCiAgICAgYnJlYWtzID0gMjApCiAgCiAgICAgCiMgSGlzdG9ncmFtIGZvciBIZWlnaHQKaGlzdChuaGFuZXMkYm9keS53ZWlnaHQsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIEhlaWdodCIsIAogICAgIHhsYWIgPSAiSGVpZ2h0IiwgCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIGJvcmRlciA9ICJibGFjayIsIAogICAgIGJyZWFrcyA9IDIwKSAgIAogICAgIApgYGAKCk1vc3QgcGFydGljaXBhbnRzIGRvIG5vdCBoYXZlIGhpZ2ggYmxvb2QgcHJlc3N1cmUuIFRoZSBhZ2Ugb2YgcGFydGljaXBhbnRzIHZhcmllcyBmcm9tIGFib3V0IDIwIHRvIGFib3V0IDkwLiBUaGUgaGVpZ2h0IGRpc3RyaWJ1dGlvbiBpcyByb3VnaGx5IHN5bW1ldHJpYy4gVGhlIGJvZHkgd2VpZ2h0IGRpc3RyaWJ1dGlvbiBpcyBza2V3ZWQgcmlnaHQgZHVlIHRvIHNvbWUgaGlnaCBvdXRsaWVycy4gVGhlIGhpZ2ggb3V0bGllcnMgbWF5IGluZGljYXRlIG9iZXNpdHkuIAoKCgpgYGB7cn0KCnBhcihtZnJvdyA9IGMoMiwyKSkKCmhpc3QobmhhbmVzJGF2Zy5zYnAsIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBtYWluID0gIlN5c3RvbGljIEJQIiwKICAgICB4bGFiID0gIlN5c3RvbGljIEJQIiwgeWxhYiA9ICJGcmVxdWVuY3kiLAogICAgIGJyZWFrcyA9IDMwKQoKaGlzdChuaGFuZXMkYXZnLmRicCwgY29sID0gInN0ZWVsYmx1ZSIsIAogICAgIG1haW4gPSAiRGlhc3RvbGljIEJQIiwKICAgICB4bGFiID0gIkRpYXN0b2xpYyBCUCIsIHlsYWIgPSAiRnJlcXVlbmN5IiwKICAgICBicmVha3MgPSAzMCkKCmhpc3QobmhhbmVzJGNob2xlc3Ryb2wsIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBtYWluID0gIkNob2xlc3Rlcm9sIiwKICAgICB4bGFiID0gIkNob2xlc3Rlcm9sIiwgeWxhYiA9ICJGcmVxdWVuY3kiLAogICAgIGJyZWFrcyA9IDMwKQoKCmBgYAoKU3lzdG9saWMgQlAgYW5kIGNob2xlc3Rlcm9sIGhhdmUgc2tld2VkIHJpZ2h0IGRpc3RyaWJ1dGlvbnMuIERpYXN0b2xpYyBCUCBpcyByb3VnaGx5IHN5bW1ldHJpYy4gT2YgdGhlIDc5MjcgcGFydGljaXBhbnRzLCAxOTY0IGFyZSBvYmVzZS4gCgoKIyMgUmVsYXRpb25zaGlwIGJldHdlZW4gRmVhdHVyZXMKCkV4YW1pbmluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHVzaW5nIHNjYXR0ZXJwbG90cyBhbmQgYSBjb3JyZWxhdGlvbiBtYXRyaXggcHJvdmlkZXMgYSBjb21wcmVoZW5zaXZlIGFwcHJvYWNoIHRvIHVuZGVyc3RhbmRpbmcgYXNzb2NpYXRpb25zLiBTY2F0dGVycGxvdHMgdmlzdWFsbHkgcmV2ZWFsIGhvdyB0d28gY29udGludW91cyB2YXJpYWJsZXMgaW50ZXJhY3QsIGFsbG93aW5nIHVzIHRvIGlkZW50aWZ5IHRyZW5kcywgY29ycmVsYXRpb25zLCBhbmQgcG90ZW50aWFsIG91dGxpZXJzLiBNZWFud2hpbGUsIGEgY29ycmVsYXRpb24gbWF0cml4IHF1YW50aWZpZXMgdGhlIHN0cmVuZ3RoIGFuZCBkaXJlY3Rpb24gb2YgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIG11bHRpcGxlIHZhcmlhYmxlcyBhdCBvbmNlLCBoZWxwaW5nIHRvIHBpbnBvaW50IHdoaWNoIHBhaXJzIGFyZSBzdHJvbmdseSBjb3JyZWxhdGVkLiBUb2dldGhlciwgc2NhdHRlcnBsb3RzIGFuZCBhIGNvcnJlbGF0aW9uIG1hdHJpeCBvZmZlciB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIGluIHRoZSBkYXRhLCBndWlkaW5nIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIG1vZGVsIHNlbGVjdGlvbi4KCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBTYW1wbGUgNTAwIHJvd3MgKGFkanVzdCB0aGUgbnVtYmVyIGFzIG5lZWRlZCkKc2FtcGxlX2luZGljZXMgPC0gc2FtcGxlKG5yb3cobmhhbmVzKSwgc2l6ZSA9IDUwMCwgcmVwbGFjZSA9IFRSVUUpCnNhbXBsZWRfZGF0YSA8LSBuaGFuZXNbc2FtcGxlX2luZGljZXMsIGMoImFnZSIsICJib2R5LndlaWdodCIsICJoZWlnaHQiLCAiYXZnLnNicCIsICJhdmcuZGJwIiwgImNob2xlc3Ryb2wiKV0KCiMgQ3JlYXRlIGEgcGFpcndpc2Ugc2NhdHRlcnBsb3QgbWF0cml4IHdpdGggdGhlIHNhbXBsZWQgZGF0YQpwYWlycyhzYW1wbGVkX2RhdGEsIAogICAgICBtYWluID0gIlBhaXJ3aXNlIFNjYXR0ZXJwbG90cyAoQm9vdHN0cmFwcGVkIFNhbXBsZSkiLAogICAgICBwY2ggPSAxOSwgICAgICAgICAgICMgU2hhcGUgb2YgdGhlIHBvaW50cyAoY2lyY2xlKQogICAgICBjb2wgPSByZ2IoMCwgMCwgMSwgMC41KSAgIyBDb2xvciB3aXRoIHRyYW5zcGFyZW5jeSAoYmx1ZSkKKQoKYGBgCgoKCmBgYHtyfQoKbmhhbmVzX3N1YnNldCA8LSBuaGFuZXNbLCBjKCJhZ2UiLCAiYm9keS53ZWlnaHQiLCAiaGVpZ2h0IiwgImF2Zy5zYnAiLCAiYXZnLmRicCIsICJjaG9sZXN0cm9sIildCgojIENvbXB1dGUgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApjb3JfbWF0cml4IDwtIGNvcihuaGFuZXNfc3Vic2V0LCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCiMgUHJpbnQgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeApwcmludChjb3JfbWF0cml4KQoKY29ycnBsb3QoY29yX21hdHJpeCwgbWV0aG9kID0gImNvbG9yIiwgdHlwZSA9ICJ1cHBlciIsIHRsLmNvbCA9ICJibGFjayIsIHRsLmNleCA9IDAuOCkKCgpgYGAKClRoZSBtb3N0IGhpZ2hseSBjb3JyZWxhdGUgZmVhdHVyZXMgYXJlIGFnZSBhbmQgYXZlcmFnZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgYm9keSB3ZWlnaHQgYW5kIGhlaWdodCwgYXZlcmFnZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBhbmQgYXZlcmFnZSBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGFnZSBhbmQgY2hvbGVzdGVyb2wsIGJvZHkgd2VpZ2h0IGFuZCBjaG9sZXN0ZXJvbCwgYW5kIGJvZHkgd2VpZ2ggYW5kIGF2ZXJhZ2UgZGlhc3RvbGljIGJsb29kIHByZXNzdXJlLiBUaGUgcGFpcndpc2UgcmVsYXRpb25zaGlwcyBhcmUgZGlzcGxheWVkIHdpdGggaW5kaXZpZHVhbCBzY2F0dGVyIHBsb3RzLiAKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBCb290c3RyYXAgYSBzbWFsbGVyIHNhbXBsZSAoYWRqdXN0IHNpemUgYXMgbmVlZGVkKQpzYW1wbGVfaW5kaWNlcyA8LSBzYW1wbGUobnJvdyhuaGFuZXMpLCBzaXplID0gNTAwLCByZXBsYWNlID0gVFJVRSkKc2FtcGxlZF9kYXRhIDwtIG5oYW5lc1tzYW1wbGVfaW5kaWNlcywgXQoKIyBTZXQgdXAgYSAyeDMgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDIsIDMpKQoKIyBTY2F0dGVycGxvdCBmb3IgQWdlIGFuZCBBdmcuIFN5c3RvbGljIEJsb29kIFByZXNzdXJlCnBsb3Qoc2FtcGxlZF9kYXRhJGFnZSwgc2FtcGxlZF9kYXRhJGF2Zy5zYnAsIG1haW4gPSAiQWdlIHZzIEF2Zy4gU3lzdG9saWMgQlAiLCAKICAgICB4bGFiID0gIkFnZSIsIHlsYWIgPSAiQXZnLiBTeXN0b2xpYyBCUCIsIGNvbCA9ICJibHVlIiwgcGNoID0gMTkpCgojIFNjYXR0ZXJwbG90IGZvciBCb2R5IFdlaWdodCBhbmQgSGVpZ2h0CnBsb3Qoc2FtcGxlZF9kYXRhJGJvZHkud2VpZ2h0LCBzYW1wbGVkX2RhdGEkaGVpZ2h0LCBtYWluID0gIkJvZHkgV2VpZ2h0IHZzIEhlaWdodCIsIAogICAgIHhsYWIgPSAiQm9keSBXZWlnaHQiLCB5bGFiID0gIkhlaWdodCIsIGNvbCA9ICJyZWQiLCBwY2ggPSAxOSkKCiMgU2NhdHRlcnBsb3QgZm9yIEF2Zy4gU3lzdG9saWMgQmxvb2QgUHJlc3N1cmUgYW5kIEF2Zy4gRGlhc3RvbGljIEJsb29kIFByZXNzdXJlCnBsb3Qoc2FtcGxlZF9kYXRhJGF2Zy5zYnAsIHNhbXBsZWRfZGF0YSRhdmcuZGJwLCBtYWluID0gIkF2Zy4gU3lzdG9saWMgdnMgQXZnLiBEaWFzdG9saWMgQlAiLCAKICAgICB4bGFiID0gIkF2Zy4gU3lzdG9saWMgQlAiLCB5bGFiID0gIkF2Zy4gRGlhc3RvbGljIEJQIiwgY29sID0gImZvcmVzdGdyZWVuIiwgcGNoID0gMTkpCgojIFNjYXR0ZXJwbG90IGZvciBBZ2UgYW5kIENob2xlc3Rlcm9sCnBsb3Qoc2FtcGxlZF9kYXRhJGFnZSwgc2FtcGxlZF9kYXRhJGNob2xlc3Ryb2wsIG1haW4gPSAiQWdlIHZzIENob2xlc3Rlcm9sIiwgCiAgICAgeGxhYiA9ICJBZ2UiLCB5bGFiID0gIkNob2xlc3Rlcm9sIiwgY29sID0gInB1cnBsZSIsIHBjaCA9IDE5KQoKIyBTY2F0dGVycGxvdCBmb3IgQXZnLiBTeXN0b2xpYyBCUCBhbmQgQ2hvbGVzdGVyb2wKcGxvdChzYW1wbGVkX2RhdGEkYXZnLnNicCwgc2FtcGxlZF9kYXRhJGNob2xlc3Ryb2wsIG1haW4gPSAiQXZnIFN5c3RvbGljIEJQIHZzIENob2xlc3Rlcm9sIiwgCiAgICAgeGxhYiA9ICJDaG9sZXN0ZXJvbCIsIHlsYWIgPSAiQXZnLlNCUCIsIGNvbCA9ICJnb2xkIiwgcGNoID0gMTkpCgojIFNjYXR0ZXJwbG90IGZvciBCb2R5IFdlaWdodCBhbmQgQXZnLiBEaWFzdG9saWMgQmxvb2QgUHJlc3N1cmUKcGxvdChzYW1wbGVkX2RhdGEkYm9keS53ZWlnaHQsIHNhbXBsZWRfZGF0YSRhdmcuZGJwLCBtYWluID0gIkJvZHkgV2VpZ2h0IHZzIEF2Zy4gRGlhc3RvbGljIEJQIiwgCiAgICAgeGxhYiA9ICJCb2R5IFdlaWdodCIsIHlsYWIgPSAiQXZnLiBEaWFzdG9saWMgQlAiLCBjb2wgPSAibWFyb29uIiwgcGNoID0gMTkpCmBgYAoKU2l4IG9mIHRoZSBtb3N0IGNvcnJlbGF0ZWQgcGFpcndpc2UgcmVsYXRpb25zaGlwIGFyZSB2aXN1YWxpemVkIGluIHNjYXR0ZXIgcGxvdHMuIEFsbCBwYWlyd2lzZSByZWxhdGlvbnNoaXBzIGFyZSBsaW5lYXIgd2l0aCBubyBhcHBhcmVudCBjbHVzdGVyaW5nLiAKCgpgYGB7cn0KCiMgU2V0IHVwIGEgMngyIHBsb3R0aW5nIGdyaWQKcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKc2V4X2hicF90YWJsZSA8LSB0YWJsZShuaGFuZXMkc2V4LCBuaGFuZXMkaGJwKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KHNleF9oYnBfdGFibGUsIAogICAgICAgICAgIGNvbCA9IGMoImdvbGQiLCAicm95YWxibHVlIiksIAogICAgICAgICAgIG1haW4gPSAiU2V4IGFuZCBIaWdoIEJsb29kIFByZXNzdXJlIiwKICAgICAgICAgICB4bGFiID0gIlNleCBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkhCUCBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgoKIyBDcmVhdGUgYSBjb250aW5nZW5jeSB0YWJsZQpyYWNlX2hicF90YWJsZSA8LSB0YWJsZShuaGFuZXMkcmFjZSwgbmhhbmVzJGhicCkKCiMgR2VuZXJhdGUgbW9zYWljIHBsb3QKbW9zYWljcGxvdChyYWNlX2hicF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJSYWNlIGFuZCBIaWdoIEJsb29kIFByZXNzdXJlIiwKICAgICAgICAgICB4bGFiID0gIlJhY2UiLCAKICAgICAgICAgICB5bGFiID0gIkhCUCBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgoKIyBDcmVhdGUgYSBjb250aW5nZW5jeSB0YWJsZQpjdXJyZW50LnNta19oYnBfdGFibGUgPC0gdGFibGUobmhhbmVzJGN1cnJlbnQuc21rLCBuaGFuZXMkaGJwKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KGN1cnJlbnQuc21rX2hicF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJDdXJyZW50IFNtb2tpbmcgU3RhdHVzIGFuZCBIQlAiLAogICAgICAgICAgIHhsYWIgPSAiQ3VycmVudCBTbW9raW5nIFN0YXR1cyIsIAogICAgICAgICAgIHlsYWIgPSAiSEJQIFN0YXR1cyIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCnBhc3Quc21rX2hicF90YWJsZSA8LSB0YWJsZShuaGFuZXMkcGFzdC5zbWssIG5oYW5lcyRoYnApCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3QocGFzdC5zbWtfaGJwX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIlBhc3QgU21va2luZyBTdGF0dXMgYW5kIEhCUCIsCiAgICAgICAgICAgeGxhYiA9ICJQYXN0IFNtb2tpbmcgU3RhdHVzIiwgCiAgICAgICAgICAgeWxhYiA9ICJIQlAgU3RhdHVzIiwKICAgICAgICAgICBib3JkZXIgPSAiYmxhY2siKQpgYGAKClJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhpZ2ggYmxvb2QgcHJlc3N1cmUgYW5kIHJhY2UsIHNleCwgYW5kIGN1cnJlbnQgc21va2luZyBzdGF0dXMgY2FuIGJlIHZpc3VhbGl6ZWQgaW4gdGhlIG1vc2FpYyBwbG90cy4gVGhlcmUgYXBwZWFycyB0byBiZSBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIGhpZ2ggY3VycmVudCBzbW9raW5nIGFuZCByYWNlIHdpdGggaGlnaCBibG9vZCBwcmVzc3VyZS4gQSBsZXNzIHByb25vdW5jZWQgcmVsYXRpb25zaGlwIGNhbiBiZWVuIHNlZW4gYmV0d2VlbiBzZXggYW5kIHJhY2Ugd2l0aCBoaWdoIGJsb29kIHByZXNzdXJlLiAKCgpgYGB7cn0KCiMgU2V0IHVwIGEgMngyIHBsb3R0aW5nIGdyaWQKcGFyKG1mcm93ID0gYygxLCAyKSkKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCmN1cnJlbnQuc21rX3JhY2VfdGFibGUgPC0gdGFibGUobmhhbmVzJHJhY2UsIG5oYW5lcyRjdXJyZW50LnNtaykKCiMgR2VuZXJhdGUgbW9zYWljIHBsb3QKbW9zYWljcGxvdChjdXJyZW50LnNta19yYWNlX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIlJhY2UgYW5kIEN1cnJlbnQgU21va2UgU3RhdHVzIiwKICAgICAgICAgICB4bGFiID0gIlJhY2UiLCAKICAgICAgICAgICB5bGFiID0gIkN1cnJlbnQgU21va2UiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgoKIyBDcmVhdGUgYSBjb250aW5nZW5jeSB0YWJsZQpjdXJyZW50LnNta19zZXhfdGFibGUgPC0gdGFibGUobmhhbmVzJHNleCwgbmhhbmVzJGN1cnJlbnQuc21rKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KGN1cnJlbnQuc21rX3NleF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJTZXggYW5kIEN1cnJlbnQgU21va2UgU3RhdHVzIiwKICAgICAgICAgICB4bGFiID0gIlNleCIsIAogICAgICAgICAgIHlsYWIgPSAiQ3VycmVudCBTbW9rZSIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKcGFzdC5zbWtfcmFjZV90YWJsZSA8LSB0YWJsZShuaGFuZXMkcmFjZSwgbmhhbmVzJHBhc3Quc21rKQoKCgpgYGAKCkFuIGFzc29jaWF0aW9uIGV4aXN0cyBiZXR3ZWVuIHJhY2UgYW5kIGN1cnJlbnQgc21va2luZy4gQmxhY2sgaW5kaXZpZHVhbHMgdGVuZCB0byBzbW9rZSBtb3JlIHRoYW4gd2hpdGUgb3Igb3RoZXIgaW5kaXZpZHVhbHMuIEFuIGFzc29jaWF0aW9uIGV4aXN0cyBiZXR3ZWVuIHNleCBhbmQgY3VycmVudCBzbW9rZSBzdGF0dXMuIEZlbWFsZSB0ZW5kIHRvIHNtb2tlIGN1cnJlbnRseSBzbGlnaHQgbW9yZSB0aGFuIG1hbGVzIGluIHRoaXMgcG9wdWxhdGlvbi4gCgoKCiMgTUlTU0lORyBWQUxVRVMKCiMjIEltcHV0YXRpb24gZm9yIENhdGVnb3JpY2FsIEZlYXR1cmVzCgpUaGUgZGlzdHJpYnV0aW9uIG9mIG1pc3NpbmcgdmFsdWVzIGZvciBpcyBleGFtaW5lZCB0byBkZXRlcm1pbmUgYW55IHBhdHRlcm5zLiBUaGlzIHN0ZXAgaXMgZG9uZSB0byBlbnN1cmUgdGhhdCB2YWx1ZSBhcmUgbWlzc2luZyBhdCByYW5kb20uIAoKYGBge3J9CiMgU2V0IHVwIGEgMngyIHBsb3R0aW5nIGdyaWQKcGFyKG1mcm93ID0gYygyLCAyKSkKCiNzdW0oaXMubmEobmhhbmVzJHJhY2UpKQoKI21lYW4oaXMubmEobmhhbmVzJHJhY2UpKQoKI3RhYmxlKG5oYW5lcyRyYWNlLCB1c2VOQSA9ICJpZmFueSIpCgoKI2dncGxvdChuaGFuZXMsIGFlcyh4ID0gZmFjdG9yKHJhY2UpKSkgKwogICNnZW9tX2JhcihmaWxsID0gImJsdWUiKSArCiAgI2xhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFJhY2UgKEJlZm9yZSBJbXB1dGF0aW9uKSIsIHggPSAiUmFjZSIsIHkgPSAiQ291bnQiKSArCiAgI3RoZW1lX21pbmltYWwoKQoKCmFnZ3IobmhhbmVzLCBjb2wgPSBjKCJuYXZ5Ymx1ZSIsICJyZWQiKSwgbnVtYmVycyA9IFRSVUUsIHNvcnRWYXJzID0gVFJVRSwgCiAgICAgbGFiZWxzID0gbmFtZXMobmhhbmVzKSwgY2V4LmF4aXMgPSAwLjcsIGdhcCA9IDIsIAogICAgIHlsYWIgPSBjKCJNaXNzaW5nIERhdGEgT3ZlcnZpZXciLCAiUGF0dGVybiBvZiBNaXNzaW5nIERhdGEiKSkKCmBgYAoKQSBwYXR0ZXJuIG9mIG1pc3NpbmcgdmFsdWVzIGV4aXN0cyBiZXR3ZWVuIGJvZHkgd2VpZ2h0LCBCTUksIGFuZCBvYmVzZS4gVGhpcyBwYXR0ZXJuIGNhbiBiZSBleHBsYWluZWQgYnkgdGhlIGZhY3QgdGhhdCBib2R5IHdlaWdodCBpcyB1c2VkIHRvIGNhbGN1bGF0ZSBCTUksIHdoaWNoIGlzIHRoZW4gdXNlZCB0byBjbGFzc2lmeSBvYnNlcnZhdGlvbnMgZm9yIG9iZXNlLiBIb3dldmVyLCB0aGUgcGF0dGVybiBvZiBtaXNzaW5nIHZhbHVlIGFwcGVhcnMgdG8gYmUgcmFuZG9tIGZvciB0aGUgb3RoZXIgZmVhdHVyZXMuIAoKTm8gcGF0dGVybiBvZiBtaXNzaW5nIHZhbHVlcyBleGlzdHMgYmV0d2VlbiByYWNlIGFuZCBvdGhlciBmZWF0dXJlcywgSW4gdHVybiwga05OIGltcHV0YXRpb24gaXMgdXNlZCBmb3IgbWlzc2luZyB2YWx1ZXMgb2YgcmFjZS4gIAoKCgpgYGB7cn0KCnN1bShpcy5uYShuaGFuZXMkcmFjZSkpCnRhYmxlKG5oYW5lcyRyYWNlLCB1c2VOQSA9ICJpZmFueSIpICAjIENoZWNrIGRpc3RyaWJ1dGlvbiBpbmNsdWRpbmcgTkFzCgoKIyBQZXJmb3JtIEtOTiBJbXB1dGF0aW9uIChzaW5nbGUgaW1wdXRhdGlvbikKbmhhbmVzX2ltcHV0ZWQgPC0ga05OKG5oYW5lcywgdmFyaWFibGUgPSAicmFjZSIsIGsgPSAzKQoKIyBLZWVwIG9ubHkgdGhlIG9yaWdpbmFsIGNvbHVtbnMgKHdpdGhvdXQgZXh0cmEgY29sdW1ucyBhZGRlZCBieSBrTk4pCm5oYW5lc19pbXB1dGVkIDwtIG5oYW5lc19pbXB1dGVkWywgbmFtZXMobmhhbmVzKV0KCgoKIyBDT01QQVJFIGJlZm9yZSBhbmQgYWZ0ZXIgaW1wdXRhdGlvbiBkaXN0cmlidXRpb24KCnBhcihtZnJvdyA9IGMoMSwyKSkgICMgU3BsaXQgcGxvdHRpbmcgYXJlYSBpbnRvIHR3bwoKIyBDb252ZXJ0IHJhY2UgdG8gYSBmYWN0b3IgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uCm5oYW5lcyRyYWNlIDwtIGFzLmZhY3RvcihuaGFuZXMkcmFjZSkKbmhhbmVzX2ltcHV0ZWQkcmFjZSA8LSBhcy5mYWN0b3IobmhhbmVzX2ltcHV0ZWQkcmFjZSkKCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiB0byBpbmRpY2F0ZSB3aGV0aGVyIHRoZSBkYXRhIGlzIG9yaWdpbmFsIG9yIGltcHV0ZWQKbmhhbmVzJHNvdXJjZSA8LSAiQmVmb3JlIEltcHV0YXRpb24iCm5oYW5lc19pbXB1dGVkJHNvdXJjZSA8LSAiQWZ0ZXIgSW1wdXRhdGlvbiIKCiMgQ29tYmluZSBib3RoIGRhdGFzZXRzCm5oYW5lc19jb21wYXJlIDwtIGJpbmRfcm93cyhuaGFuZXMsIG5oYW5lc19pbXB1dGVkKQoKIyBQbG90IHRoZSBkaXN0cmlidXRpb24gYmVmb3JlIGFuZCBhZnRlciBpbXB1dGF0aW9uCmdncGxvdChuaGFuZXNfY29tcGFyZSwgYWVzKHggPSByYWNlLCBmaWxsID0gc291cmNlKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFJhY2UgQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiUmFjZSIsCiAgICAgICB5ID0gIkNvdW50IiwKICAgICAgIGZpbGwgPSAiRGF0YXNldCIpICsKICB0aGVtZV9taW5pbWFsKCkKCmltcHV0ZWRfbmhhbmVzIDwtIG5oYW5lc19pbXB1dGVkCgoKYGBgClRoZSBvdmVyYWxsIGRpc3RyaWJ1dGlvbiBvZiByYWNlIGFmdGVyIGltcHV0YXRpb24gY2hhbmdlZCBzbGlnaHRseSBhZnRlciBpbXB1dGF0aW9uLgoKCiMjIFJlZ3Jlc3Npb24tQmFzZWQgSW1wdXRhdGlvbiBmb3IgTnVtZXJpY2FsIEZlYXR1cmUKClRoZSBwYXR0ZXJuIG9mIG1pc3NpbmcgdmFsdWVzIGZvciBib2R5IHdlaWdodCBhbmQgY2hvbGVzdGVyb2wgaXMgZXhhbWluZWQgZm9yIGFueSBwb3RlbnRpYWwgcGF0dGVybnMuCgpgYGB7cn0KCgojdmlzX21pc3MobmhhbmVzWywgYygiYm9keS53ZWlnaHQiLCAiY2hvbGVzdHJvbCIpXSkKCgojIFNldCBzbWFsbGVyIGZvbnQgc2l6ZSBmb3IgdGhlIHBsb3QKcGFyKGNleCA9IDAuNCkgICMgQWRqdXN0IHRoaXMgdmFsdWUgZm9yIGRlc2lyZWQgZm9udCBzaXplCgojIEdlbmVyYXRlIHRoZSBtaXNzaW5nIGRhdGEgcGF0dGVybiBwbG90CgptZC5wYXR0ZXJuKGltcHV0ZWRfbmhhbmVzKQoKIyBSZXNldCBncmFwaGljcyBzZXR0aW5ncyAob3B0aW9uYWwpCnBhcihjZXggPSAxKQoKCmBgYApBIHBhdHRlcm4gb2YgbWlzc2luZyB2YWx1ZXMgZXhpc3RzIGJldHdlZW4gYm9keSB3ZWlnaHQsIEJNSSwgYW5kIG9iZXNlIGJlY2F1c2UKCgpCb2R5IHdlaWdodCBpcyBjb3JyZWxhdGVkIHdpdGggaGVpZ2h0LCBhdmVyYWdlIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSwgYW5kIHNleC4gSW4gdHVybiwgdGhlIG1pc3NpbmcgYm9keSB3ZWlnaHQgdmFsdWVzIHdpbGwgYmUgaW1wdXRlZCB1c2luZyBhIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgc2FpZCBleHBsYW5hdG9yeSBmZWF0dXJlcy4gCgpgYGB7cn0KCiMgU3Vic2V0IG5oYW5lcyB0byBjb250YWluIG9ubHkgY29tcGxldGUgb2JzZXJ2YXRpb25zIGZvciBzZWxlY3RlZCB2YXJpYWJsZXMKY29tcGxldGVfbmhhbmVzIDwtIGltcHV0ZWRfbmhhbmVzW2NvbXBsZXRlLmNhc2VzKGltcHV0ZWRfbmhhbmVzWywgYygiYm9keS53ZWlnaHQiLCAiaGVpZ2h0IiwgImF2Zy5kYnAiLCAic2V4IildKSwgXQoKIyBGaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsCm1vZGVsX2JvZHkud2VpZ2h0IDwtIGxtKGJvZHkud2VpZ2h0IH4gaGVpZ2h0ICsgYXZnLmRicCArIHNleCwgZGF0YSA9IGNvbXBsZXRlX25oYW5lcykKCiMgVmlldyB0aGUgbW9kZWwgc3VtbWFyeQpzdW1tYXJ5KG1vZGVsX2JvZHkud2VpZ2h0KQoKIyBQbG90IHRoZSByZXNpZHVhbHMKcGxvdChtb2RlbF9ib2R5LndlaWdodCRyZXNpZHVhbHMpCgoKYGBgCgoKVGhlIHJlc2lkdWFsIHBsb3Qgc2hvd3Mgbm8gcGF0dGVybnMuIEluIHR1cm4sIHRoZSBtb2RlbCBmaXQgdGhlIGRhdGEgd2VsbCBhbmQgdGhlIGFzc3VtcHRpb24gb2YgY29uc3RhbnQgdmFyaWFuY2Ugb2YgcmVzaWR1YWxzIGlzIHNhdGlzZmllZC4KCmBgYHtyfQoKIyBDcmVhdGUgYSBjb3B5IG9mIHRoZSBkYXRhc2V0IHRvIHdvcmsgd2l0aAojaW1wdXRlZF9uaGFuZXMgPC0gY29tcGxldGVfbmhhbmVzCgpjb2xTdW1zKGlzLm5hKGltcHV0ZWRfbmhhbmVzKSkKIyBJZGVudGlmeSB0aGUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGZvciBib2R5LndlaWdodAptaXNzaW5nX2JvZHlfd2VpZ2h0IDwtIGlzLm5hKGltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0KQoKIyBVc2UgdGhlIG1vZGVsIHRvIHByZWRpY3QgdGhlIG1pc3NpbmcgdmFsdWVzCmltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0W21pc3NpbmdfYm9keV93ZWlnaHRdIDwtIHByZWRpY3QobW9kZWxfYm9keS53ZWlnaHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBpbXB1dGVkX25oYW5lc1ttaXNzaW5nX2JvZHlfd2VpZ2h0LCBjKCJoZWlnaHQiLCAiYXZnLmRicCIsICJzZXgiLCAiYWdlIildKQoKIyBDaGVjayB0aGUgZGF0YXNldAojaGVhZChpbXB1dGVkX25oYW5lcykKCiMgQ3JlYXRlIGEgcGxvdCB0byBvdmVybGF5IHRoZSBkZW5zaXR5IGN1cnZlcwpnZ3Bsb3QoKSArCiAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBjb21wbGV0ZV9uaGFuZXMsIGFlcyh4ID0gYm9keS53ZWlnaHQpLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuMiwgbmEucm0gPSBUUlVFKSArIAogIGdlb21fZGVuc2l0eShkYXRhID0gaW1wdXRlZF9uaGFuZXMsIGFlcyh4ID0gYm9keS53ZWlnaHQpLCBmaWxsID0gInJlZCIsIGFscGhhID0gMC4yLCBuYS5ybSA9IFRSVUUpICsKICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBCb2R5IFdlaWdodDogQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiQm9keSBXZWlnaHQiLCB5ID0gIkRlbnNpdHkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lID0gIkJvZHkgV2VpZ2h0IiwgdmFsdWVzID0gYygiYmx1ZSIgPSAiYmx1ZSIsICJyZWQiID0gInJlZCIpLCAKICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJCZWZvcmUgSW1wdXRhdGlvbiIsICJBZnRlciBJbXB1dGF0aW9uIikpCgoKYGBgCgoKVGhlIGRpc3RyaWJ1dGlvbiBiZWZvcmUgYW5kIGFmdGVyIGltcHV0YXRpb24gaXMgc2ltaWxhci4gSW4gdHVybiwgdGhlIGltcHV0YXRpb24gZGlkIG5vdCBjaGFuZ2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiBib2R5IHdlaWdodC4gCgoKCgpUaGUgZmVhdHVyZXMgdGhhdCBhcmUgbW9zdCBjb3JyZWxhdGVkIHRvIGNob2xlc3Rlcm9sIGFyZSBhZ2UsIGF2ZXJhZ2Ugc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGFuZCBib2R5IHdlaWdodC4gSW4gdHVybiwgdGhlIG1pc3NpbmcgdmFsdWVzIG9mIGNob2xlc3Rlcm9sIHdpbGwgYmUgaW1wdXRlZCB3aXRoIGEgbW9kZWwgdXNpbmcgdGhlc2UgZmVhdHVyZXMuIAoKYGBge3J9CgojIFN1YnNldCBuaGFuZXMgdG8gaW5jbHVkZSBvbmx5IHJvd3Mgd2l0aCBjb21wbGV0ZSBjYXNlcyBmb3IgY2hvbGVzdHJvbCwgYWdlLCBhdmcuc2JwLCBhbmQgQk1JCmNvbXBsZXRlX25oYW5lcyA8LSBpbXB1dGVkX25oYW5lc1shaXMubmEoaW1wdXRlZF9uaGFuZXMkY2hvbGVzdHJvbCkgJiAhaXMubmEoaW1wdXRlZF9uaGFuZXMkYWdlKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoaW1wdXRlZF9uaGFuZXMkYXZnLnNicCkgJiAhaXMubmEoaW1wdXRlZF9uaGFuZXMkYm9keS53ZWlnaHQpLCBdCgpjb21wbGV0ZV9uaGFuZXMgPC0gaW1wdXRlZF9uaGFuZXNbY29tcGxldGUuY2FzZXMoaW1wdXRlZF9uaGFuZXNbLCBjKCJjaG9sZXN0cm9sIiwgImFnZSIsICJhdmcuc2JwIiwgImJvZHkud2VpZ2h0IildKSwgXQoKIyBGaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgY2hvbGVzdHJvbAptb2RlbF9jaG9sZXN0cm9sIDwtIGxtKGNob2xlc3Ryb2wgfiBhZ2UgKyBhdmcuc2JwICsgYm9keS53ZWlnaHQsIGRhdGEgPSBjb21wbGV0ZV9uaGFuZXMpCnN1bW1hcnkobW9kZWxfY2hvbGVzdHJvbCkKCgojIFZpZXcgdGhlIG1vZGVsIHN1bW1hcnkKc3VtbWFyeShtb2RlbF9jaG9sZXN0cm9sKQoKIyBDcmVhdGUgYSBjb3B5IG9mIG5oYW5lcyBmb3IgaW1wdXRhdGlvbgojaW1wdXRlZF9uaGFuZXMgPC0gbmhhbmVzCgojIElkZW50aWZ5IHJvd3Mgd2l0aCBtaXNzaW5nIGNob2xlc3Ryb2wgdmFsdWVzCm1pc3NpbmdfY2hvbGVzdHJvbCA8LSBpcy5uYShpbXB1dGVkX25oYW5lcyRjaG9sZXN0cm9sKQoKIyBVc2UgdGhlIG1vZGVsIHRvIHByZWRpY3QgdGhlIG1pc3NpbmcgY2hvbGVzdHJvbCB2YWx1ZXMKaW1wdXRlZF9uaGFuZXMkY2hvbGVzdHJvbFttaXNzaW5nX2Nob2xlc3Ryb2xdIDwtIHByZWRpY3QobW9kZWxfY2hvbGVzdHJvbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gaW1wdXRlZF9uaGFuZXNbbWlzc2luZ19jaG9sZXN0cm9sLCBjKCJhZ2UiLCAiYXZnLnNicCIsICJib2R5LndlaWdodCIpXSkKCgpgYGAKCkFsbCBleHBsYW5hdG9yeSBmZWF0dXJlcyBhcmUgc2lnbmlmaWNhbnQuIAoKYGBge3J9CiMgR2V0IHJlc2lkdWFscyBmcm9tIHRoZSBtb2RlbCBmb3IgY29tcGxldGVfbmhhbmVzCnJlc2lkdWFsc19jaG9sZXN0cm9sIDwtIHJlc2lkdWFscyhtb2RlbF9jaG9sZXN0cm9sKQoKIyBQcmVkaWN0ZWQgdmFsdWVzIGZvciBjb21wbGV0ZV9uaGFuZXMKcHJlZGljdGVkX2Nob2xlc3Ryb2wgPC0gcHJlZGljdChtb2RlbF9jaG9sZXN0cm9sLCBuZXdkYXRhID0gY29tcGxldGVfbmhhbmVzKQoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciByZXNpZHVhbHMgYW5kIHByZWRpY3RlZCB2YWx1ZXMKcmVzaWR1YWxzX2RhdGEgPC0gZGF0YS5mcmFtZSgKICBwcmVkaWN0ZWRfY2hvbGVzdHJvbCA9IHByZWRpY3RlZF9jaG9sZXN0cm9sLAogIHJlc2lkdWFsc19jaG9sZXN0cm9sID0gcmVzaWR1YWxzX2Nob2xlc3Ryb2wKKQoKIyBDcmVhdGUgdGhlIHJlc2lkdWFsIHBsb3QKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocmVzaWR1YWxzX2RhdGEsIGFlcyh4ID0gcHJlZGljdGVkX2Nob2xlc3Ryb2wsIHkgPSByZXNpZHVhbHNfY2hvbGVzdHJvbCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArICAjIFBsb3QgdGhlIHJlc2lkdWFscwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsgICMgQWRkIGEgaG9yaXpvbnRhbCBsaW5lIGF0IDAKICBsYWJzKHRpdGxlID0gIlJlc2lkdWFsIFBsb3QgZm9yIENob2xlc3Ryb2wgUHJlZGljdGlvbiIsCiAgICAgICB4ID0gIlByZWRpY3RlZCBDaG9sZXN0cm9sIiwKICAgICAgIHkgPSAiUmVzaWR1YWxzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBDcmVhdGUgdGhlIG92ZXJsYXkgZGVuc2l0eSBwbG90CmdncGxvdCgpICsKICBnZW9tX2RlbnNpdHkoZGF0YSA9IG5oYW5lcywgYWVzKHggPSBjaG9sZXN0cm9sKSwgZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjIsIG5hLnJtID0gVFJVRSkgKyAgIyBCZWZvcmUgaW1wdXRhdGlvbgogIGdlb21fZGVuc2l0eShkYXRhID0gaW1wdXRlZF9uaGFuZXMsIGFlcyh4ID0gY2hvbGVzdHJvbCksIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjIsIG5hLnJtID0gVFJVRSkgKyAgIyBBZnRlciBpbXB1dGF0aW9uCiAgbGFicyh0aXRsZSA9ICJEZW5zaXR5IFBsb3Qgb2YgQ2hvbGVzdHJvbDogQmVmb3JlIGFuZCBBZnRlciBJbXB1dGF0aW9uIiwKICAgICAgIHggPSAiQ2hvbGVzdHJvbCIsIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiQ2hvbGVzdHJvbCIsIHZhbHVlcyA9IGMoImJsdWUiID0gImJsdWUiLCAicmVkIiA9ICJyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQmVmb3JlIEltcHV0YXRpb24iLCAiQWZ0ZXIgSW1wdXRhdGlvbiIpKQoKCmBgYAoKVGhlIGRpc3RyaWJ1dGlvbiBvZiBjaG9sZXN0ZXJvbCBkaWQgbm90IGNoYW5nZSBzaWduaWZpY2FudGx5IGFmdGVyIGltcHV0YXRpb24uIEFzIHdlbGwsIHRoZSByZXNpZHVhbCBwbG90IGRvZXMgbm90IGRpc3BsYXkgYW55IHBhdHRlcm5zLiBJbiB0dXJuLCB0aGUgbW9kZWwgZml0IHRoZSBkYXRhIHdlbGwgYW5kIHRoZSBhc3N1bXB0aW9uIG9mIGNvbnN0YW50IHZhcmlhbmNlIG9mIHJlc2lkdWFscyBpcyBzYXRpc2ZpZWQuIAoKIyMgRmVhdHVyZSBFbmdpbmVlcgoKSW4gdHVybiBhIGZlYXR1cmUgd2hpY2ggY2FsY3VsYXRlcyBCTUkgaXMgY3JlYXRlZCB0byBkZXRlcm1pbmUgb2JzZXJ2YXRpb25zIGFyZSBjbGFzc2lmaWVkIGFzIG9iZXNlLiBBY2NvcmRpbmcgdG8gdGhlIENEQyAoaHR0cHM6Ly93d3cuY2RjLmdvdi9uY2NkcGhwL2RucGFvL2RhdGEtdHJlbmRzLW1hcHMvaGVscC9ucGFvX2R0bS9kZWZpbml0aW9ucy5odG1sKSwgQWR1bHQgb2Jlc2l0eSBpcyBkZWZpbmVkIGFzIGJvZHkgbWFzcyBpbmRleCAoQk1JKSDiiaUgMzAuMC4gQW5vdGhlciBmZWF0dXJlIG5hbWVkIG9iZXNlLCBiYXNlZCBvbiBCTUksIGlzIGNyZWF0ZWQsIHdoZXJlIDAgPSBubyBhbmQgMSA9IHllcy4gCgpgYGB7cn0KCiMgQ3JlYXRlIEJNSSBmZWF0dXJlCmltcHV0ZWRfbmhhbmVzJEJNSSA8LSAoaW1wdXRlZF9uaGFuZXMkYm9keS53ZWlnaHQgKiA3MDMpIC8gKGltcHV0ZWRfbmhhbmVzJGhlaWdodCAqIGltcHV0ZWRfbmhhbmVzJGhlaWdodCkKIyBSb3VuZCB0aGUgQk1JIHRvIHRoZSBuZWFyZXN0IG9uZXMgcGxhY2UKaW1wdXRlZF9uaGFuZXMkQk1JIDwtIHJvdW5kKGltcHV0ZWRfbmhhbmVzJEJNSSwgMCkKCiMgQ3JlYXRlIHRoZSBvYmVzZSBmZWF0dXJlIGJhc2VkIG9uIEJNSQppbXB1dGVkX25oYW5lcyRvYmVzZSA8LSBpZmVsc2UoaW1wdXRlZF9uaGFuZXMkQk1JID49IDMwLCAxLCAwKQoKYGBgCgpgYGB7cn0KCnBhcihtZnJvdyA9IGMoMSwgMikpCgojIEhpc3RvZ3JhbSBmb3IgQk1JCgpoaXN0KGltcHV0ZWRfbmhhbmVzJEJNSSwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQk1JIiwgCiAgICAgeGxhYiA9ICJCTUkiLCAKICAgICBjb2wgPSAic3RlZWxibHVlIiwgCiAgICAgYm9yZGVyID0gImJsYWNrIiwgCiAgICAgYnJlYWtzID0gMjApCgojIGJhciBjaGFydCBmb3Igb2Jlc2UKbmhhbmVzJG9iZXNlIDwtIGZhY3RvcihpbXB1dGVkX25oYW5lcyRvYmVzZSwgbGV2ZWxzID0gYygwLCAxKSwgbGFiZWxzID0gYygiTm8iLCAiWWVzIikpCgpvYmVzZV9jb3VudCA8LSB0YWJsZShpbXB1dGVkX25oYW5lcyRvYmVzZSkKCmJhcnBsb3QoaGVpZ2h0ID0gb2Jlc2VfY291bnQsIGNvbCA9ICJzdGVlbGJsdWUiLCBtYWluID0gIk9iZXNpdHkiLCB4bGFiID0gIk9iZXNpdHkiLCB5bGFiID0gIkNvdW50IikKCgpgYGAKCgpgYGB7cn0KIyBTZXQgdXAgYSAyeDIgcGxvdHRpbmcgZ3JpZApwYXIobWZyb3cgPSBjKDIsIDIpKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKb2Jlc2VfaGJwX3RhYmxlIDwtIHRhYmxlKGltcHV0ZWRfbmhhbmVzJG9iZXNlLCBpbXB1dGVkX25oYW5lcyRoYnApCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3Qob2Jlc2VfaGJwX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIk9iZXNpdHkgYW5kIEhpZ2ggQmxvb2QgUHJlc3N1cmUiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkhCUCBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCm9iZXNlX3NleF90YWJsZSA8LSB0YWJsZShpbXB1dGVkX25oYW5lcyRzZXgsIGltcHV0ZWRfbmhhbmVzJG9iZXNlKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KG9iZXNlX3NleF90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJTZXggYW5kIE9iZXNpdHkiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIlNleCIsCiAgICAgICAgICAgYm9yZGVyID0gImJsYWNrIikKCgojIENyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlCm9iZXNlX3JhY2VfdGFibGUgPC0gdGFibGUoaW1wdXRlZF9uaGFuZXMkcmFjZSwgaW1wdXRlZF9uaGFuZXMkb2Jlc2UpCgojIEdlbmVyYXRlIG1vc2FpYyBwbG90Cm1vc2FpY3Bsb3Qob2Jlc2VfcmFjZV90YWJsZSwgCiAgICAgICAgICAgY29sID0gYygiZ29sZCIsICJyb3lhbGJsdWUiKSwgCiAgICAgICAgICAgbWFpbiA9ICJSYWNlIGFuZCBPYmVzaXR5IiwKICAgICAgICAgICB4bGFiID0gIk9iZXNpdHkgU3RhdHVzIiwgCiAgICAgICAgICAgeWxhYiA9ICJSYWNlIiwKICAgICAgICAgICBib3JkZXIgPSAiYmxhY2siKQoKCiMgQ3JlYXRlIGEgY29udGluZ2VuY3kgdGFibGUKb2Jlc2Vfc21va2VfdGFibGUgPC0gdGFibGUoaW1wdXRlZF9uaGFuZXMkY3VycmVudC5zbWssIGltcHV0ZWRfbmhhbmVzJG9iZXNlKQoKIyBHZW5lcmF0ZSBtb3NhaWMgcGxvdAptb3NhaWNwbG90KG9iZXNlX3Ntb2tlX3RhYmxlLCAKICAgICAgICAgICBjb2wgPSBjKCJnb2xkIiwgInJveWFsYmx1ZSIpLCAKICAgICAgICAgICBtYWluID0gIkN1cnJlbnQgU21va2luZyBTdGF0dXMgYW5kIE9iZXNpdHkiLAogICAgICAgICAgIHhsYWIgPSAiT2Jlc2l0eSBTdGF0dXMiLCAKICAgICAgICAgICB5bGFiID0gIkN1cnJlbnQgU21va2luZyBTdGF0dXMiLAogICAgICAgICAgIGJvcmRlciA9ICJibGFjayIpCgoKYGBgCgoKCgoKIyMgTXVsdGlwbGUgSW1wdXRhdGlvbgoKTXVsdGlwbGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyAoTUlDRSkgaXMgYSBwb3dlcmZ1bCB0ZWNobmlxdWUgdXNlZCB0byBoYW5kbGUgbWlzc2luZyBkYXRhIGJ5IGNyZWF0aW5nIG11bHRpcGxlIHBsYXVzaWJsZSBpbXB1dGF0aW9ucyBmb3IgZWFjaCBtaXNzaW5nIHZhbHVlIGJhc2VkIG9uIHJlbGF0aW9uc2hpcHMgaW4gdGhlIG9ic2VydmVkIGRhdGEuIEZvciBib2R5IHdlaWdodCwgY2hvbGVzdGVyb2wsIGFuZCByYWNlLCBNSUNFIGNhbiBiZSBhcHBsaWVkIHRvIGdlbmVyYXRlIGFjY3VyYXRlIGltcHV0YXRpb25zLCBhY2NvdW50aW5nIGZvciBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGVzZSB2YXJpYWJsZXMgYW5kIG90aGVyIGZlYXR1cmVzIGluIHRoZSBkYXRhc2V0LiBUaGlzIG1ldGhvZCBhbGxvd3MgZm9yIG1vcmUgcmVsaWFibGUgc3RhdGlzdGljYWwgYW5hbHlzZXMgYnkgcmVkdWNpbmcgYmlhcyBhbmQgcHJlc2VydmluZyB2YXJpYWJpbGl0eSBpbiB0aGUgaW1wdXRlZCB2YWx1ZXMsIG1ha2luZyBpdCBlc3BlY2lhbGx5IHVzZWZ1bCBmb3IgaGFuZGxpbmcgbWlzc2luZyBkYXRhIGluIGluY29taW5nIG5ldyBkYXRhIHNldHMuCgpgYGB7cn0KCm5oYW5lczEkcmFjZSA8LSBhcy5mYWN0b3IobmhhbmVzMSRyYWNlKSAgCm5oYW5lczEkY2hvbGVzdHJvbCA8LSBhcy5udW1lcmljKG5oYW5lczEkY2hvbGVzdHJvbCkKbmhhbmVzMSRib2R5LndlaWdodCA8LSBhcy5udW1lcmljKG5oYW5lczEkYm9keS53ZWlnaHQpCgpuaGFuZXMyIDwtIG5oYW5lczEKCgojIDEgaW5pdGlhbGl6YXRpb24KCmluaXQgPC0gbWljZShuaGFuZXMyLCBtYXhpdD0wKQpwcmludChpbml0JG1ldGhvZCkKCgojIDIgaW1wdXRhdGlvbiBtb2RlbHMKCmltcCA8LSBtaWNlKG5oYW5lczIsIG1ldGhvZCA9IGMoIiIsICIiLCAiIiwgIiIsICIiLCAiIiwgIiIsICJwb2x5cmVnIiwgInBtbSIsICIiLCAiIiwgIiIsICIiLCAiIiwgInBtbSIsICIiKSwgCiAgICAgICAgICAgIG1heGl0PTEwLAogICAgICAgICAgICBtPTEsCiAgICAgICAgICAgIHNlZWQgPSAxMjMsCiAgICAgICAgICAgIHByaW50PUYpCgpjb21wbGV0ZShpbXAsIGFjdGlvbiA9IDEpWzE6MTAsXQoKCiMgMyBtdWx0aXBsZSAoaXRlcmF0aXZlKSBpbXB1dGF0aW9ucwoKaW1wNSA8LSBtaWNlKG5oYW5lczIsIG1ldGhvZCA9ICJwbW0iLCBtID0gNSwgbWF4aXQgPSAxMCwgc2VlZCA9IDEyMywgcHJpbnQ9RikKcGxvdChpbXA1KQoKCgpgYGAKCgpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBib2R5IHdlaWdodCB2YXJpYWJsZS4gVG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHRoZSBpbXB1dGF0aW9ucywgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSB3aWxsIGJlIGNhbGN1bGF0ZWQgYW5kIGNvbXBhcmVkIHRoZSBNU0Ugb2YgdGhlIHNpbmdsZSBpbXB1dGF0aW9uIGNvbmR1Y3RlZCBmb3IgYm9keSB3ZWlnaHQgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzLCBwcm92aWRpbmcgYSBtZWFzdXJlIG9mIHRoZSBpbXB1dGF0aW9uIGFjY3VyYWN5LgoKCmBgYHtyfQptb2RlbDVfYm9keXdlaWdodCA8LSB3aXRoKGltcDUsIGxtKGJvZHkud2VpZ2h0IH4gaGVpZ2h0ICsgYXZnLmRicCArIHNleCkpCnN1bW1hcnkuc3RhdHMgPSBzdW1tYXJ5KG1vZGVsNV9ib2R5d2VpZ2h0KQoKc3VtbWFyeShwb29sKG1vZGVsNV9ib2R5d2VpZ2h0KSkKCmJldGEgPSBzdW1tYXJ5LnN0YXRzJGVzdGltYXRlW3NlcSgxLDE1LGJ5PTMpXSAgIyBleHBsaWNpdCB2ZWN0b3I6IGMoMSw0LDcsMTAsMTMpCmJldGEudmFyID0gKHN1bW1hcnkuc3RhdHMkc3RkLmVycm9yW3NlcSgxLDE1LGJ5PTMpXSleMiAKUSA9IG1lYW4oYmV0YSkKVSA9IG1lYW4oYmV0YS52YXIpCkIgPSB2YXIoYmV0YSkKVCA9IFUgKyAoNi81KSpCCnBvb2wuc2UgPSBzcXJ0KFQpCmNiaW5kKHBvb2wuc2UuaW50ZXJjZXB0ID0gcG9vbC5zZSkKCgpgYGAKVGhlIG1vZGVsIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgYm9keSB3ZWlnaHQgZm9yIGluY29taW5nIG5ldyBkYXRhLiAKClRoZSBNU0UgZm9yIHNpbmdsZSBib2R5IHdlaWdodCBpbXB1dGF0aW9uIHVzZWQgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzIGlzIE1TRSA9IChSU0UpXjIgPSAoMzMuNTIpXjIgPSAxMTIzLjYgd2hpY2ggaXMgc3Vic3RhbnRpYWxseSBoaWdoZXIgdGhhbiBNU0Ugd2hlbiB1c2luZyBNSUNFLiBJbiB0dXJuLCBNSUNFIGlzIHJlY29tbWVuZGVkIGluIHRoaXMgYW5hbHlzaXMuIAoKCgpNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjaG9sZXN0ZXJvbCB2YXJpYWJsZS4gVG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHRoZSBpbXB1dGF0aW9ucywgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSB3aWxsIGJlIGNhbGN1bGF0ZWQgYW5kIGNvbXBhcmVkIHRoZSBNU0Ugb2YgdGhlIHNpbmdsZSBpbXB1dGF0aW9uIGNvbmR1Y3RlZCBmb3IgY2hvbGVzdGVyb2wgZWFybGllciBpbiB0aGlzIGFuYWx5c2lzLCBwcm92aWRpbmcgYSBtZWFzdXJlIG9mIHRoZSBpbXB1dGF0aW9uIGFjY3VyYWN5LgoKYGBge3J9Cm1vZGVsNV9jaG9sIDwtIHdpdGgoaW1wNSwgbG0oY2hvbGVzdHJvbCB+IGFnZSArIGF2Zy5zYnApKQpzdW1tYXJ5LnN0YXRzID0gc3VtbWFyeShtb2RlbDVfY2hvbCkKCgpzdW1tYXJ5KHBvb2wobW9kZWw1X2Nob2wpKQoKYmV0YSA9IHN1bW1hcnkuc3RhdHMkZXN0aW1hdGVbc2VxKDEsMTUsYnk9MyldICAjIGV4cGxpY2l0IHZlY3RvcjogYygxLDQsNywxMCwxMykKYmV0YS52YXIgPSAoc3VtbWFyeS5zdGF0cyRzdGQuZXJyb3Jbc2VxKDEsMTUsYnk9MyldKV4yIApRID0gbWVhbihiZXRhKQpVID0gbWVhbihiZXRhLnZhcikKQiA9IHZhcihiZXRhKQpUID0gVSArICg2LzUpKkIKcG9vbC5zZSA9IHNxcnQoVCkKY2JpbmQocG9vbC5zZS5pbnRlcmNlcHQgPSBwb29sLnNlKQoKCmBgYApUaGUgbW9kZWwgY2FuIGJlIHVzZWQgdG8gcHJlZGljdCBjaG9sZXN0ZXJvbCBmb3IgaW5jb21pbmcgbmV3IGRhdGEuIAoKVGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBmcm9tIHRoZSBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKSBnaXZlbiBpbiB0aGUgbW9kZWwgb3V0cHV0IGZyb20gdGhlIGNob2xlc3Rlcm9sIHNpbmdsZSBpbXB1dGF0aW9uIG1ldGhvZCB1c2VkIGVhcmx5IGluIHRoaXMgYW5hbHlzaXMgaXMgTVNFID0gKDQyLjU5KV4yID0gMTgxMy45LiBUaHVzLCB0aGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpID0gMTgxMy45LgoKVGhlIE1TRSB1c2luZyBNSUNFIGlzIDMuNjYgd2hpY2ggaXMgc3Vic3RhbnRpYWxseSBsZXNzLiBUaGVyZWZvcmUsIE1JQ0UgaXMgcmVjb21tZW5kZWQgaW4gdGhlIGFuYWx5c2lzIG9mIHRoaXMgZGF0YSBzZXQuIAoKCgpGb3IgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSByYWNlIHZhcmlhYmxlLCBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSB3aWxsIGJlIGFwcGxpZWQgdXNpbmcgbXVsdGlub21pYWwgbG9naXN0aWMgcmVncmVzc2lvbi4gVGhpcyBtZXRob2Qgd2lsbCBhY2NvdW50IGZvciB0aGUgY2F0ZWdvcmljYWwgbmF0dXJlIG9mIHRoZSByYWNlIHZhcmlhYmxlLCBhbGxvd2luZyBmb3IgYXBwcm9wcmlhdGUgaW1wdXRhdGlvbiBiYXNlZCBvbiB0aGUgcmVsYXRpb25zaGlwcyB3aXRoIG90aGVyIGZlYXR1cmVzIGluIHRoZSBkYXRhc2V0LgoKYGBge3J9Cgptb2RlbF9yYWNlIDwtIHdpdGgoaW1wNSwgbXVsdGlub20ocmFjZSB+IGF2Zy5kYnAgKyBhdmcuc2JwICsgY2hvbGVzdHJvbCArIGhicCkpCgojIFBvb2wgdGhlIHJlc3VsdHMgZnJvbSB0aGUgaW1wdXRlZCBkYXRhc2V0cwpwb29sZWRfcmFjZSA8LSBwb29sKG1vZGVsX3JhY2UpCgojIEdldCBhIHN1bW1hcnkgb2YgdGhlIHBvb2xlZCByZXN1bHRzCnN1bW1hcnkocG9vbGVkX3JhY2UpCgpgYGAKClRoZSBtb2RlbCBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHJhY2UgZm9yIGluY29taW5nIG5ldyBkYXRhLiAKCgojIEZFQVRVUkUgVFJBTlNGT1JNSU5HCgpCYXNlZCBvbiB0aGUgZGlzdHJpYnV0aW9uIHBhdHRlcm5zIG9ic2VydmVkIGluIHRoZSBOSEFORVMgZGF0YSBzZXQsIHNldmVyYWwgZmVhdHVyZSBlbmdpbmVlcmluZyB0YXNrcyBjYW4gYmUgcGVyZm9ybWVkIHRvIGVuaGFuY2UgdGhlIGRhdGHigJlzIHVzYWJpbGl0eSBmb3IgbW9kZWxpbmcuIEZpcnN0LCBmb3IgYWdlLCB3aGljaCBpcyByb3VnaGx5IHVuaWZvcm0sIG5vIG1ham9yIHRyYW5zZm9ybWF0aW9uIGlzIG5lZWRlZCwgdGhvdWdoIHdlIG1heSBjb25zaWRlciBiaW5uaW5nIGl0IGludG8gYWdlIGdyb3VwcyBpZiBpdCBoZWxwcyB3aXRoIGludGVycHJldGFiaWxpdHkuIEZvciB3ZWlnaHQgYW5kIHN5c3RvbGljIGJsb29kIHByZXNzdXJlIChCUCksIHdoaWNoIGFyZSBib3RoIHNrZXdlZCByaWdodCwgYSBsb2cgdHJhbnNmb3JtYXRpb24gY291bGQgYmUgYXBwbGllZCB0byByZWR1Y2Ugc2tld25lc3MgYW5kIG1ha2UgdGhlc2UgdmFyaWFibGVzIG1vcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZS4gU2ltaWxhcmx5LCBmb3IgY2hvbGVzdGVyb2wsIHdoaWNoIGlzIHNsaWdodGx5IHNrZXdlZCByaWdodCwgYSBsb2cgdHJhbnNmb3JtYXRpb24gbWF5IGFsc28gYmUgdXNlZnVsLiBGb3IgZGlhc3RvbGljIEJQLCB3aGljaCBpcyByb3VnaGx5IHN5bW1ldHJpYywgbm8gdHJhbnNmb3JtYXRpb24gaXMgbmVjZXNzYXJ5LCBidXQgY2hlY2tpbmcgZm9yIG91dGxpZXJzIGNvdWxkIGJlIGhlbHBmdWwuIFRoZXNlIHRyYW5zZm9ybWF0aW9ucyBhaW0gdG8gc3RhYmlsaXplIHZhcmlhbmNlLCByZWR1Y2UgdGhlIGltcGFjdCBvZiBleHRyZW1lIHZhbHVlcywgYW5kIGNyZWF0ZSBmZWF0dXJlcyB0aGF0IGFyZSBtb3JlIHN1aXRhYmxlIGZvciBtb2RlbGluZyB3aXRoIHRlY2huaXF1ZXMgbGlrZSBsaW5lYXIgcmVncmVzc2lvbiBvciBvdGhlciBzdGF0aXN0aWNhbCBtZXRob2RzLgoKCmBgYHtyfQojTG9nIHRyYW5zZm9ybWF0aW9uIHRvIHJlZHVjZSBza2V3CgppbXB1dGVkX25oYW5lcyRsb2dfYm9keS53ZWlnaHQgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJGJvZHkud2VpZ2h0KQppbXB1dGVkX25oYW5lcyRsb2dfYXZnLnNicCA8LSBsb2coaW1wdXRlZF9uaGFuZXMkYXZnLnNicCkKaW1wdXRlZF9uaGFuZXMkbG9nX2Nob2xlc3Ryb2wgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJGNob2xlc3Ryb2wpCmltcHV0ZWRfbmhhbmVzJGxvZ19CTUkgPC0gbG9nKGltcHV0ZWRfbmhhbmVzJEJNSSkKCgoKCiMgU2V0IHVwIHRoZSBwbG90dGluZyBhcmVhIHRvIHNob3cgMngyIGdyaWQgb2YgaGlzdG9ncmFtcwpwYXIobWZyb3cgPSBjKDIsIDIpKSAgIyBUaGlzIHdpbGwgY3JlYXRlIGEgMngyIGdyaWQgb2YgcGxvdHMKCiMgSGlzdG9ncmFtIGZvciBMb2ctQ2hvbGVzdGVyb2wKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfY2hvbGVzdHJvbCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTG9nKENob2xlc3Rlcm9sKSIsIAogICAgIHhsYWIgPSAiTG9nKENob2xlc3Rlcm9sKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkgIAogICAgCiMgSGlzdG9ncmFtIGZvciBMb2ctQm9keSBXZWlnaHQKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfYm9keS53ZWlnaHQsIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIExvZyhCb2R5IFdlaWdodCkiLCAKICAgICB4bGFiID0gIkxvZyhCb2R5IFdlaWdodCkiLCAKICAgICBjb2wgPSAic3RlZWxibHVlIiwgCiAgICAgYm9yZGVyID0gImJsYWNrIiwgCiAgICAgYnJlYWtzID0gMjApIAoKCiMgSGlzdG9ncmFtIGZvciBMb2ctU3lzdG9saWMgQlAKaGlzdChpbXB1dGVkX25oYW5lcyRsb2dfYXZnLnNicCwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTG9nKFN5c3RvbGljIEJQKSIsIAogICAgIHhsYWIgPSAiTG9nKFN5c3RvbGljIEJQKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKICAgCgojIEhpc3RvZ3JhbSBmb3IgTG9nLUJNSQpoaXN0KGltcHV0ZWRfbmhhbmVzJGxvZ19CTUksIAogICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIExvZyhCTUkpIiwgCiAgICAgeGxhYiA9ICJMb2coQk1JKSIsIAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLCAKICAgICBib3JkZXIgPSAiYmxhY2siLCAKICAgICBicmVha3MgPSAyMCkKCgpgYGAKClRoZSBkaXN0cmlidXRpb25zIG9mIHRoZXNlIHByZXZpb3VzbHkgc2tld2VkIGZlYXR1cmVzIGFyZSBtb3JlIHN5bW1ldHJpYyBhZnRlciBsb2cgdHJhbnNmb3JtYXRpb25zLiAKCgojIFNUQU5EQVJESVpJTkcKClN0YW5kYXJkaXppbmcgZmVhdHVyZXMgZW5zdXJlcyB0aGF0IHZhcmlhYmxlcyB3aXRoIGRpZmZlcmVudCBzY2FsZXMsIHN1Y2ggYXMgYWdlLCBCTUksIGhlaWdodCwgYW5kIGNob2xlc3Rlcm9sLCBjb250cmlidXRlIGVxdWFsbHkgdG8gdGhlIGFuYWx5c2lzLiBXaXRob3V0IHN0YW5kYXJkaXphdGlvbiwgdmFyaWFibGVzIHdpdGggbGFyZ2VyIHJhbmdlcyBjb3VsZCBkb21pbmF0ZSBtb2RlbCBwZXJmb3JtYW5jZSwgbGVhZGluZyB0byBiaWFzZWQgcmVzdWx0cy4gQnkgc3RhbmRhcmRpemluZyB0aGVzZSBmZWF0dXJlcywgd2UgaW1wcm92ZSBtb2RlbCBhY2N1cmFjeSwgZW5hYmxlIGJldHRlciBjb21wYXJpc29uIGJldHdlZW4gdmFyaWFibGVzLCBhbmQgZW5oYW5jZSB0aGUgZWZmaWNpZW5jeSBvZiBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMsIHBhcnRpY3VsYXJseSBpbiBtb2RlbHMgdGhhdCByZWx5IG9uIGRpc3RhbmNlLWJhc2VkIG9yIGdyYWRpZW50LWJhc2VkIG1ldGhvZHMuCgoKYGBge3J9CgojIFNwZWNpZnkgdGhlIGNvbHVtbnMgdG8gYmUgc3RhbmRhcmRpemVkCmNvbHVtbnNfdG9fc3RhbmRhcmRpemUgPC0gYygiYWdlIiwgIkJNSSIsICJoZWlnaHQiLCAiYm9keS53ZWlnaHQiLCAiY2hvbGVzdHJvbCIsICJhdmcuZGJwIiwgImF2Zy5zYnAiKQoKIyBTdGFuZGFyZGl6ZSB0aGUgc2VsZWN0ZWQgY29sdW1ucwppbXB1dGVkX25oYW5lc1tjb2x1bW5zX3RvX3N0YW5kYXJkaXplXSA8LSBzY2FsZShpbXB1dGVkX25oYW5lc1tjb2x1bW5zX3RvX3N0YW5kYXJkaXplXSkKCiAgCmBgYAoKCgoKCiMgRkVBVFVSRSBTRUxFQ1RJT04KCkZlYXR1cmUgc2VsZWN0aW9uIGlzIGEgY3JpdGljYWwgc3RlcCBpbiBtb2RlbCBidWlsZGluZywgYXMgaXQgaGVscHMgaW1wcm92ZSBtb2RlbCBwZXJmb3JtYW5jZSBieSBpZGVudGlmeWluZyB0aGUgbW9zdCByZWxldmFudCBwcmVkaWN0b3JzIGFuZCByZWR1Y2luZyBvdmVyZml0dGluZy4gSW4gdGhpcyBhbmFseXNpcywgYm90aCBtb2RlbC1iYXNlZCBzZWxlY3Rpb24gYW5kIHJlZ3VsYXJpemF0aW9uLWJhc2VkIHNlbGVjdGlvbiB0ZWNobmlxdWVzIHdpbGwgYmUgdXNlZCB0byBhc3Nlc3MgYW5kIHJldGFpbiB0aGUgbW9zdCBpbmZsdWVudGlhbCBmZWF0dXJlcyBmb3IgYWNjdXJhdGUgcHJlZGljdGlvbiwgZW5zdXJpbmcgYSBtb3JlIGVmZmljaWVudCBhbmQgaW50ZXJwcmV0YWJsZSBtb2RlbC4KCgpBIGZ1bGwgbW9kZWwgaXMgY3JlYXRlZCB0byBwcmVkaWN0IGNob2xlc3Rlcm9sLgoKYGBge3J9Cgojc2FwcGx5KGltcHV0ZWRfbmhhbmVzLCBmdW5jdGlvbih4KSBsZW5ndGgodW5pcXVlKHgpKSkKCiNoZWFkKGltcHV0ZWRfbmhhbmVzLCAyNSkKCgojaGVhZChpbXB1dGVkX25oYW5lcykKCgptb2RlbF9mdWxsIDwtIGxtKGNob2xlc3Ryb2wgfiBhZ2UgKyByYWNlICsgQk1JICsgaGVpZ2h0ICsgYm9keS53ZWlnaHQgKyAKICAgICAgICAgICAgICAgICAgaGJwICsgYXZnLmRicCArIGF2Zy5zYnAgKyBvYmVzZSwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbXB1dGVkX25oYW5lcykKCiMgU3VtbWFyeSBvZiB0aGUgZnVsbCBtb2RlbAojc3VtbWFyeShtb2RlbF9mdWxsKQoKIyBUaWR5IHRoZSBtb2RlbCBzdW1tYXJ5Cm1vZGVsX3N1bW1hcnkgPC0gdGlkeShtb2RlbF9mdWxsKQoKIyBDYWxjdWxhdGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpCm1zZSA8LSBtZWFuKG1vZGVsX2Z1bGwkcmVzaWR1YWxzXjIpCgojIENhbGN1bGF0ZSBSLXNxdWFyZWQKcjIgPC0gc3VtbWFyeShtb2RlbF9mdWxsKSRyLnNxdWFyZWQKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgTVNFIGFuZCBSLXNxdWFyZWQKbWV0cmljc19kZiA8LSBkYXRhLmZyYW1lKFN0YXRpc3RpYyA9IGMoIk1lYW4gU3F1YXJlZCBFcnJvciIsICJSLXNxdWFyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZSA9IGMobXNlLCByMikpCgojIFByaW50IHRoZSBtb2RlbCBzdW1tYXJ5IHVzaW5nIGthYmxlCmthYmxlKG1vZGVsX3N1bW1hcnksIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiUmVncmVzc2lvbiBNb2RlbCBTdW1tYXJ5IikKCiMgUHJpbnQgTVNFIGFuZCBSLXNxdWFyZWQgdXNpbmcga2FibGUKa2FibGUobWV0cmljc19kZiwgZGlnaXRzID0gMywgY2FwdGlvbiA9ICJNb2RlbCBQZXJmb3JtYW5jZSBNZXRyaWNzIikKCmBgYAoKU3RlcHdpc2UgbW9kZWwgc2VsZWN0aW9uIGlzIGNvbmR1Y3RlZCB0byByZXRhaW4gc2lnbmlmaWNhbnQgcHJlZGljdG9ycy4gU2VsZWN0aW9uIHByb2Nlc3NlcyBiYXNlZCBvbiBBSUMgYW5kIEJJQyBhcmUgY29uZHVjdGVkLiAKCmBgYHtyfQojIFN0ZXB3aXNlIG1vZGVsIHNlbGVjdGlvbiBiYXNlZCBvbiBBSUMKbW9kZWxfc3RlcHdpc2VfQUlDIDwtIHN0ZXAobW9kZWxfZnVsbCwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IDApCgojIFN0ZXB3aXNlIG1vZGVsIHNlbGVjdGlvbiBiYXNlZCBvbiBCSUMKbW9kZWxfc3RlcHdpc2VfQklDIDwtIHN0ZXAobW9kZWxfZnVsbCwgZGlyZWN0aW9uID0gImJvdGgiLCBrID0gbG9nKG5yb3coaW1wdXRlZF9uaGFuZXMpKSwgdHJhY2UgPSAwKQoKCiMgVGlkeSB0aGUgc3VtbWFyaWVzIGZvciBib3RoIG1vZGVscwpzdGVwd2lzZV9BSUNfc3VtbWFyeSA8LSB0aWR5KG1vZGVsX3N0ZXB3aXNlX0FJQykKc3RlcHdpc2VfQklDX3N1bW1hcnkgPC0gdGlkeShtb2RlbF9zdGVwd2lzZV9CSUMpCgojIENvbXB1dGUgTVNFIGZvciBib3RoIG1vZGVscwptc2VfQUlDIDwtIG1lYW4obW9kZWxfc3RlcHdpc2VfQUlDJHJlc2lkdWFsc14yKQptc2VfQklDIDwtIG1lYW4obW9kZWxfc3RlcHdpc2VfQklDJHJlc2lkdWFsc14yKQoKIyBDb21wdXRlIFItc3F1YXJlZCBmb3IgYm90aCBtb2RlbHMKcjJfQUlDIDwtIHN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQUlDKSRyLnNxdWFyZWQKcjJfQklDIDwtIHN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQklDKSRyLnNxdWFyZWQKCiMgQ3JlYXRlIGRhdGEgZnJhbWVzIGZvciBNU0UgYW5kIFItc3F1YXJlZAptZXRyaWNzX0FJQ19kZiA8LSBkYXRhLmZyYW1lKFN0YXRpc3RpYyA9IGMoIk1lYW4gU3F1YXJlZCBFcnJvciIsICJSLXNxdWFyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmFsdWUgPSBjKG1zZV9BSUMsIHIyX0FJQykpCgptZXRyaWNzX0JJQ19kZiA8LSBkYXRhLmZyYW1lKFN0YXRpc3RpYyA9IGMoIk1lYW4gU3F1YXJlZCBFcnJvciIsICJSLXNxdWFyZWQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmFsdWUgPSBjKG1zZV9CSUMsIHIyX0JJQykpCgojIFByaW50IHRoZSBzdGVwd2lzZSBBSUMgbW9kZWwgc3VtbWFyeQprYWJsZShzdGVwd2lzZV9BSUNfc3VtbWFyeSwgZGlnaXRzID0gMywgY2FwdGlvbiA9ICJTdGVwd2lzZSBBSUMgUmVncmVzc2lvbiBNb2RlbCBTdW1tYXJ5IikKCiMgUHJpbnQgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgQUlDIG1vZGVsCmthYmxlKG1ldHJpY3NfQUlDX2RmLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0gIk1vZGVsIFBlcmZvcm1hbmNlIE1ldHJpY3MgKEFJQykiKQoKIyBQcmludCB0aGUgc3RlcHdpc2UgQklDIG1vZGVsIHN1bW1hcnkKa2FibGUoc3RlcHdpc2VfQklDX3N1bW1hcnksIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiU3RlcHdpc2UgQklDIFJlZ3Jlc3Npb24gTW9kZWwgU3VtbWFyeSIpCgojIFByaW50IHBlcmZvcm1hbmNlIG1ldHJpY3MgZm9yIEJJQyBtb2RlbAprYWJsZShtZXRyaWNzX0JJQ19kZiwgZGlnaXRzID0gMywgY2FwdGlvbiA9ICJNb2RlbCBQZXJmb3JtYW5jZSBNZXRyaWNzIChCSUMpIikKYGBgCgoKCmBgYHtyfQojIENoZWNrIFItc3F1YXJlZCB2YWx1ZQpzdW1tYXJ5KG1vZGVsX3N0ZXB3aXNlX0FJQykkci5zcXVhcmVkCnN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQklDKSRyLnNxdWFyZWQKCgpgYGAKCkJvdGggQUlDIGFuZCBCSUMgc3RlcHdpc2UgbW9kZWwgc2VsZWN0aW9uIHByb2Nlc3NlcyBoYXZlIHNpbWlsYXIgUiBzcXVhcmVkIHZhbHVlcy4gVGhlIEJJQyBzdGVwd2lzZSBtb2RlbCBpcyBwYXJzaW1vbmlvdXMuIFRoaXMgbW9kZWwgc2VsZWN0cyBhZ2UsIGhlaWdodCwgYm9keSB3ZWlnaHQsIGFuZCBhdmVyYWdlIGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSBhcyBwcmVkaWN0b3JzIGZvciBjaG9sZXN0ZXJvbC4gCgoKClJlZ3VsYXJpemF0aW9uLWJhc2VkIHNlbGVjdGlvbiB0ZWNobmlxdWVzIGFyZSB1c2VmdWwgZm9yIGltcHJvdmluZyBtb2RlbCBwZXJmb3JtYW5jZSBhbmQgcHJldmVudGluZyBvdmVyZml0dGluZywgZXNwZWNpYWxseSB3aGVuIHlvdSBoYXZlIG1hbnkgcHJlZGljdG9ycy4gVHdvIGNvbW1vbiByZWd1bGFyaXphdGlvbiBtZXRob2RzIGFyZSBSaWRnZSByZWdyZXNzaW9uIGFuZCBMYXNzbyByZWdyZXNzaW9uLgoKYGBge3J9CgojIEV4dHJhY3QgdGhlIHJlc3BvbnNlIGFuZCBwcmVkaWN0b3JzCnkgPC0gaW1wdXRlZF9uaGFuZXMkY2hvbGVzdHJvbCAgIyBSZXNwb25zZSB2YXJpYWJsZQpYIDwtIGFzLm1hdHJpeChpbXB1dGVkX25oYW5lc1ssIGMoImFnZSIsICJCTUkiLCAiaGVpZ2h0IiwgImJvZHkud2VpZ2h0IiwgImF2Zy5kYnAiLCAiYXZnLnNicCIpXSkKCiMgT3B0aW9uYWw6IFNjYWxlIHByZWRpY3RvcnMgKHJlY29tbWVuZGVkIGZvciBSaWRnZSBhbmQgTGFzc28pClhfc2NhbGVkIDwtIHNjYWxlKFgpCgojIEZpdCBSaWRnZSBhbmQgTGFzc28gbW9kZWxzCnJpZGdlX21vZGVsIDwtIGdsbW5ldChYX3NjYWxlZCwgeSwgYWxwaGEgPSAwKSAgIyBSaWRnZSBSZWdyZXNzaW9uIChhbHBoYSA9IDApCmxhc3NvX21vZGVsIDwtIGdsbW5ldChYX3NjYWxlZCwgeSwgYWxwaGEgPSAxKSAgIyBMYXNzbyBSZWdyZXNzaW9uIChhbHBoYSA9IDEpCgojIFJpZGdlIGNyb3NzLXZhbGlkYXRpb24KY3ZfcmlkZ2UgPC0gY3YuZ2xtbmV0KFhfc2NhbGVkLCB5LCBhbHBoYSA9IDApCmJlc3RfbGFtYmRhX3JpZGdlIDwtIGN2X3JpZGdlJGxhbWJkYS5taW4KCiMgTGFzc28gY3Jvc3MtdmFsaWRhdGlvbgpjdl9sYXNzbyA8LSBjdi5nbG1uZXQoWF9zY2FsZWQsIHksIGFscGhhID0gMSkKYmVzdF9sYW1iZGFfbGFzc28gPC0gY3ZfbGFzc28kbGFtYmRhLm1pbgoKIyBFeHRyYWN0IGNvZWZmaWNpZW50cyBmb3IgUmlkZ2UgYW5kIExhc3NvIGF0IG9wdGltYWwgbGFtYmRhCnJpZGdlX2NvZWZmaWNpZW50cyA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChjb2VmKGN2X3JpZGdlLCBzID0gImxhbWJkYS5taW4iKSkpCmNvbG5hbWVzKHJpZGdlX2NvZWZmaWNpZW50cykgPC0gYygiQ29lZmZpY2llbnQiKQpyaWRnZV9jb2VmZmljaWVudHMkUHJlZGljdG9yIDwtIHJvd25hbWVzKHJpZGdlX2NvZWZmaWNpZW50cykKCmxhc3NvX2NvZWZmaWNpZW50cyA8LSBhcy5kYXRhLmZyYW1lKGFzLm1hdHJpeChjb2VmKGN2X2xhc3NvLCBzID0gImxhbWJkYS5taW4iKSkpCmNvbG5hbWVzKGxhc3NvX2NvZWZmaWNpZW50cykgPC0gYygiQ29lZmZpY2llbnQiKQpsYXNzb19jb2VmZmljaWVudHMkUHJlZGljdG9yIDwtIHJvd25hbWVzKGxhc3NvX2NvZWZmaWNpZW50cykKCiMgUHJlZGljdGlvbnMgZm9yIFJpZGdlCnByZWRfcmlkZ2UgPC0gcHJlZGljdChjdl9yaWRnZSwgWF9zY2FsZWQsIHMgPSAibGFtYmRhLm1pbiIpCgojIFByZWRpY3Rpb25zIGZvciBMYXNzbwpwcmVkX2xhc3NvIDwtIHByZWRpY3QoY3ZfbGFzc28sIFhfc2NhbGVkLCBzID0gImxhbWJkYS5taW4iKQoKIyBDYWxjdWxhdGUgUk1TRSBmb3IgUmlkZ2UKcm1zZV9yaWRnZSA8LSBzcXJ0KG1lYW4oKHByZWRfcmlkZ2UgLSB5KV4yKSkKCiMgQ2FsY3VsYXRlIFJNU0UgZm9yIExhc3NvCnJtc2VfbGFzc28gPC0gc3FydChtZWFuKChwcmVkX2xhc3NvIC0geSleMikpCgojIENhbGN1bGF0ZSBSwrIgZm9yIFJpZGdlCnJzc19yaWRnZSA8LSBzdW0oKHByZWRfcmlkZ2UgLSB5KV4yKSAgIyBSZXNpZHVhbCBTdW0gb2YgU3F1YXJlcwp0c3NfcmlkZ2UgPC0gc3VtKCh5IC0gbWVhbih5KSleMikgICAgIyBUb3RhbCBTdW0gb2YgU3F1YXJlcwpyMl9yaWRnZSA8LSAxIC0gcnNzX3JpZGdlIC8gdHNzX3JpZGdlCgojIENhbGN1bGF0ZSBSwrIgZm9yIExhc3NvCnJzc19sYXNzbyA8LSBzdW0oKHByZWRfbGFzc28gLSB5KV4yKQp0c3NfbGFzc28gPC0gc3VtKCh5IC0gbWVhbih5KSleMikKcjJfbGFzc28gPC0gMSAtIHJzc19sYXNzbyAvIHRzc19sYXNzbwoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciBSTVNFIGFuZCBSwrIKbWV0cmljc19kZiA8LSBkYXRhLmZyYW1lKAogIE1vZGVsID0gYygiUmlkZ2UiLCAiTGFzc28iKSwKICBSTVNFID0gYyhybXNlX3JpZGdlLCBybXNlX2xhc3NvKSwKICBSMiA9IGMocjJfcmlkZ2UsIHIyX2xhc3NvKQopCgojIFByaW50IFJpZGdlIGNvZWZmaWNpZW50cyB1c2luZyBrYWJsZQprYWJsZShyaWRnZV9jb2VmZmljaWVudHMsIGRpZ2l0cyA9IDMsIGNhcHRpb24gPSAiUmlkZ2UgUmVncmVzc2lvbiBDb2VmZmljaWVudHMiKQoKIyBQcmludCBMYXNzbyBjb2VmZmljaWVudHMgdXNpbmcga2FibGUKa2FibGUobGFzc29fY29lZmZpY2llbnRzLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0gIkxhc3NvIFJlZ3Jlc3Npb24gQ29lZmZpY2llbnRzIikKCiMgUHJpbnQgUk1TRSBhbmQgUsKyIHZhbHVlcyB1c2luZyBrYWJsZQprYWJsZShtZXRyaWNzX2RmLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0gIk1vZGVsIFBlcmZvcm1hbmNlIE1ldHJpY3MgKFJNU0UgJiBSwrIpIikKYGBgCgoKVGhlIFJpZGdlIGFuZCBMYXNzbyBtZXRob2QgYm90aCBoYWQgc2ltaWxhciBSIHNxdWFyZWQgdmFsdWVzLiBUaGVyZWZvcmUsIGJvdGggbW9kZWxzIGFyZSByZWNvbW1lbmRlZC4gCgojIExJTkVBUiBSRUdSRVNTSU9OCgpJbiB0aGlzIHJlZ3Jlc3Npb24gYW5hbHlzaXMgb2YgdGhlIE5IQU5FUyBkYXRhc2V0LCB0aHJlZSBtb2RlbHMgYXJlIGRldmVsb3BlZCB1c2luZyBjcm9zcyB2YWxpZGF0aW9uIHRvIHByZWRpY3QgdGhlIG91dGNvbWUgdmFyaWFibGUgY2hvbGVzdGVyb2w6IGEgZnVsbCBtb2RlbCBpbmNsdWRpbmcgYWxsIHNlbGVjdGVkIHByZWRpY3RvcnMsIGEgc3RlcHdpc2UgQklDIG1vZGVsIGZvciB2YXJpYWJsZSBzZWxlY3Rpb24sIGFuZCBhIFJpZGdlIHJlZ3Jlc3Npb24gbW9kZWwgdG8gYWRkcmVzcyBtdWx0aWNvbGxpbmVhcml0eS4gRWFjaCBtb2RlbCB3YXMgZXZhbHVhdGVkIGJhc2VkIG9uIGl0cyBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLCB3aXRoIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBhbmQgUi1zcXVhcmVkIChSXjIpIHVzZWQgYXMga2V5IGNvbXBhcmlzb24gbWV0cmljcy4gQnkgYW5hbHl6aW5nIHRoZXNlIG1vZGVscywgdGhlIGFpbSBpcyB0byBkZXRlcm1pbmUgdGhlIG1vc3QgYWNjdXJhdGUgYW5kIGVmZmljaWVudCBhcHByb2FjaCBmb3IgbW9kZWxpbmcgdGhlIGRhdGEgd2hpbGUgYmFsYW5jaW5nIGNvbXBsZXhpdHkgYW5kIHByZWRpY3RpdmUgcG93ZXIuCgpgYGB7cn0KCnNldC5zZWVkKDQyKQp0cmFpbl9jb250cm9sIDwtIHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSAxMCkKCmBgYAoKCiMjIEZ1bGwgTW9kZWwKClRoZSBmdWxsIG1vZGVsIGluY2x1ZGVzIGFsbCBzZWxlY3RlZCBwcmVkaWN0b3IgdmFyaWFibGVzLCBwcm92aWRpbmcgYSBjb21wcmVoZW5zaXZlIGFwcHJvYWNoIHRvIGVzdGltYXRpbmcgY2hvbGVzdGVyb2wgbGV2ZWxzLiBUbyBhc3Nlc3MgaXRzIHN0YWJpbGl0eSwgd2UgaW1wbGVtZW50IGstZm9sZCBjcm9zcy12YWxpZGF0aW9uLCB3aGljaCBzeXN0ZW1hdGljYWxseSBwYXJ0aXRpb25zIHRoZSBkYXRhc2V0IGludG8gbXVsdGlwbGUgdHJhaW5pbmcgYW5kIHZhbGlkYXRpb24gc3Vic2V0cy4gVGhpcyBhcHByb2FjaCBtaXRpZ2F0ZXMgdGhlIHJpc2sgb2Ygb3ZlcmZpdHRpbmcgYnkgdGVzdGluZyB0aGUgbW9kZWzigJlzIHBlcmZvcm1hbmNlIG9uIHVuc2VlbiBkYXRhLCB5aWVsZGluZyBhIHJvYnVzdCBlc3RpbWF0ZSBvZiBwcmVkaWN0aW9uIGVycm9yLiBUaGUgbW9kZWzigJlzIGVmZmVjdGl2ZW5lc3MgaXMgcXVhbnRpZmllZCB1c2luZyB0aGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpLCB3aGljaCBtZWFzdXJlcyB0aGUgYXZlcmFnZSBzcXVhcmVkIGRpZmZlcmVuY2UgYmV0d2VlbiBvYnNlcnZlZCBhbmQgcHJlZGljdGVkIGNob2xlc3Rlcm9sIHZhbHVlcy4gQnkgZW1wbG95aW5nIGNyb3NzLXZhbGlkYXRpb24sIHdlIGVuc3VyZSB0aGF0IHRoZSBmdWxsIG1vZGVsIGlzIGV2YWx1YXRlZCByaWdvcm91c2x5LCBwcm92aWRpbmcgaW5zaWdodHMgaW50byBpdHMgYWNjdXJhY3kgYW5kIHJlbGlhYmlsaXR5IGluIHJlYWwtd29ybGQgYXBwbGljYXRpb25zLgoKCmBgYHtyfQoKIyBTZXQgdXAgMTAtZm9sZCBjcm9zcy12YWxpZGF0aW9uCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSAgIyAxMC1mb2xkIENWCgojIEZpdCB0aGUgZnVsbCBtb2RlbApmdWxsX21vZGVsIDwtIHRyYWluKGNob2xlc3Ryb2wgfiBhZ2UgKyByYWNlICsgbG9nX0JNSSArIGhlaWdodCArIGxvZ19ib2R5LndlaWdodCArIAogICAgICAgICAgICAgICAgICAgIGhicCArIGF2Zy5kYnAgKyBsb2dfYXZnLnNicCArIG9iZXNlLCAKICAgICAgICAgICAgICAgICAgICBkYXRhID0gaW1wdXRlZF9uaGFuZXMsIAogICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJsbSIsIAogICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wpCgojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZyb20gdGhlIGZpbmFsIG1vZGVsCmNvZWZmX3RhYmxlIDwtIHN1bW1hcnkoZnVsbF9tb2RlbCRmaW5hbE1vZGVsKSRjb2VmZmljaWVudHMgJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUHJlZGljdG9yIikgICMgQ29udmVydCByb3cgbmFtZXMgdG8gYSBjb2x1bW4KCiMgQ2FsY3VsYXRlIENyb3NzLVZhbGlkYXRpb24gTVNFCm1zZSA8LSBtZWFuKGZ1bGxfbW9kZWwkcmVzYW1wbGUkUk1TRV4yKQoKIyBDYWxjdWxhdGUgUsKyIGZyb20gdGhlIGZpbmFsIG1vZGVsCnIyIDwtIHN1bW1hcnkoZnVsbF9tb2RlbCRmaW5hbE1vZGVsKSRyLnNxdWFyZWQKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgTVNFIGFuZCBSwrIKbWV0cmljc19kZiA8LSBkYXRhLmZyYW1lKAogIFN0YXRpc3RpYyA9IGMoIk1lYW4gU3F1YXJlZCBFcnJvciIsICJSLXNxdWFyZWQiKSwKICBWYWx1ZSA9IGMobXNlLCByMikKKQoKIyBQcmludCB0aGUgY29lZmZpY2llbnRzIHVzaW5nIGthYmxlCmthYmxlKGNvZWZmX3RhYmxlLCBkaWdpdHMgPSA0LCBjYXB0aW9uID0gIlJlZ3Jlc3Npb24gTW9kZWwgQ29lZmZpY2llbnRzIiwgZm9ybWF0ID0gIm1hcmtkb3duIikKCiMgUHJpbnQgTVNFIGFuZCBSwrIgdXNpbmcga2FibGUKa2FibGUobWV0cmljc19kZiwgZGlnaXRzID0gNCwgY2FwdGlvbiA9ICJNb2RlbCBQZXJmb3JtYW5jZSBNZXRyaWNzIChNU0UgJiBSwrIpIiwgZm9ybWF0ID0gIm1hcmtkb3duIikKYGBgCgoKCiMjIFN0ZXB3aXNlIEJJQwoKSW4gdGhpcyBhbmFseXNpcywgYSBTdGVwd2lzZSBCSUMgcmVncmVzc2lvbiBtb2RlbCBpcyBpbXBsZW1lbnRlZCB0byBpZGVudGlmeSB0aGUgbW9zdCBwYXJzaW1vbmlvdXMgc3Vic2V0IG9mIHByZWRpY3RvcnMgZm9yIGVzdGltYXRpbmcgY2hvbGVzdGVyb2wgbGV2ZWxzLiBUaGUgU3RlcHdpc2UgQklDIGFwcHJvYWNoIHNlbGVjdHMgdmFyaWFibGVzIGJ5IG1pbmltaXppbmcgdGhlIEJheWVzaWFuIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQklDKSwgd2hpY2ggcGVuYWxpemVzIG1vZGVsIGNvbXBsZXhpdHkgdG8gcHJldmVudCBvdmVyZml0dGluZy4gVG8gYXNzZXNzIHRoZSBtb2RlbOKAmXMgZ2VuZXJhbGl6YWJpbGl0eSwgd2UgZW1wbG95IGNyb3NzLXZhbGlkYXRpb24gYnkgc3BsaXR0aW5nIHRoZSBkYXRhc2V0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc3Vic2V0cy4gVGhlIG1vZGVsIGlzIHRyYWluZWQgb24gdGhlIHRyYWluaW5nIGRhdGEsIGFuZCBwcmVkaWN0aW9ucyBhcmUgbWFkZSBvbiB0aGUgdGVzdCBkYXRhIHRvIGNvbXB1dGUgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBhcyBhIG1lYXN1cmUgb2YgbW9kZWwgcGVyZm9ybWFuY2UuIEFkZGl0aW9uYWxseSwgdGhlIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBpbiBhIHN0cnVjdHVyZWQgdGFibGUgZm9yIGNsYXJpdHkgYW5kIGludGVycHJldGF0aW9uLiBUaGlzIGFwcHJvYWNoIGVuc3VyZXMgYW4gb3B0aW1hbCBiYWxhbmNlIGJldHdlZW4gbW9kZWwgY29tcGxleGl0eSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeSB3aGlsZSB2YWxpZGF0aW5nIHRoZSBtb2RlbOKAmXMgcm9idXN0bmVzcy4KCmBgYHtyfQoKc3RlcHdpc2VfYmljX21vZGVsIDwtIGZ1bmN0aW9uKHRyYWluX2RhdGEsIHRlc3RfZGF0YSkgewogICMgRml0IGluaXRpYWwgZnVsbCBtb2RlbAogIGZ1bGxfbW9kZWwyIDwtIGxtKGNob2xlc3Ryb2wgfiBhZ2UgKyBoZWlnaHQgKyBsb2dfYm9keS53ZWlnaHQgKyBhdmcuZGJwLCBkYXRhID0gdHJhaW5fZGF0YSkKICAKICAjIFBlcmZvcm0gc3RlcHdpc2Ugc2VsZWN0aW9uIHVzaW5nIEJJQwogIG1vZGVsX3N0ZXB3aXNlX0JJQyA8LSBzdGVwQUlDKGZ1bGxfbW9kZWwyLCBkaXJlY3Rpb24gPSAiYm90aCIsIGsgPSBsb2cobnJvdyh0cmFpbl9kYXRhKSksIHRyYWNlID0gRkFMU0UpCiAgCiAgIyBFeHRyYWN0IGNvZWZmaWNpZW50cwogIGNvZWZmX3RhYmxlIDwtIHN1bW1hcnkobW9kZWxfc3RlcHdpc2VfQklDKSRjb2VmZmljaWVudHMgJT4lCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gIlByZWRpY3RvciIpICAjIENvbnZlcnQgcm93IG5hbWVzIHRvIGEgY29sdW1uCgogICMgUHJpbnQgY29lZmZpY2llbnRzIHVzaW5nIGthYmxlCiAgcHJpbnQoa2FibGUoY29lZmZfdGFibGUsIGRpZ2l0cyA9IDQsIGNhcHRpb24gPSAiU3RlcHdpc2UgQklDIE1vZGVsIENvZWZmaWNpZW50cyIsIGZvcm1hdCA9ICJtYXJrZG93biIpKQoKICAjIFByZWRpY3Qgb24gdGVzdCBkYXRhCiAgcHJlZGljdGlvbnMgPC0gcHJlZGljdChtb2RlbF9zdGVwd2lzZV9CSUMsIG5ld2RhdGEgPSB0ZXN0X2RhdGEpCiAgCiAgIyBDb21wdXRlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKQogIG1zZSA8LSBtZWFuKCh0ZXN0X2RhdGEkY2hvbGVzdHJvbCAtIHByZWRpY3Rpb25zKV4yKQoKICAjIENvbXB1dGUgUsKyCiAgcnNzIDwtIHN1bSgodGVzdF9kYXRhJGNob2xlc3Ryb2wgLSBwcmVkaWN0aW9ucyleMikgICMgUmVzaWR1YWwgU3VtIG9mIFNxdWFyZXMKICB0c3MgPC0gc3VtKCh0ZXN0X2RhdGEkY2hvbGVzdHJvbCAtIG1lYW4odGVzdF9kYXRhJGNob2xlc3Ryb2wpKV4yKSAgIyBUb3RhbCBTdW0gb2YgU3F1YXJlcwogIHIyIDwtIDEgLSByc3MvdHNzICAjIFItc3F1YXJlZAogIAogICMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBmb3IgTVNFIGFuZCBSwrIKICBtZXRyaWNzX2RmIDwtIGRhdGEuZnJhbWUoCiAgICBTdGF0aXN0aWMgPSBjKCJNZWFuIFNxdWFyZWQgRXJyb3IiLCAiUi1zcXVhcmVkIiksCiAgICBWYWx1ZSA9IGMobXNlLCByMikKICApCgogICMgUHJpbnQgTVNFIGFuZCBSwrIgdXNpbmcga2FibGUKICBwcmludChrYWJsZShtZXRyaWNzX2RmLCBkaWdpdHMgPSA0LCBjYXB0aW9uID0gIlN0ZXB3aXNlIEJJQyBNb2RlbCBQZXJmb3JtYW5jZSBNZXRyaWNzIChNU0UgJiBSwrIpIiwgZm9ybWF0ID0gIm1hcmtkb3duIikpCgogIHJldHVybihsaXN0KG1vZGVsID0gbW9kZWxfc3RlcHdpc2VfQklDLCBtc2UgPSBtc2UsIHIyID0gcjIpKSAgIyBSZXR1cm4gdGhlIG1vZGVsLCBNU0UsIGFuZCBSwrIKfQoKIyBFeGFtcGxlIHVzYWdlIChBc3N1bWluZyB5b3UgaGF2ZSBhIGRhdGFzZXQgc3BsaXQgaW50byB0cmFpbl9kYXRhIGFuZCB0ZXN0X2RhdGEpCnNldC5zZWVkKDEyMykKdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihpbXB1dGVkX25oYW5lcyRjaG9sZXN0cm9sLCBwID0gMC44LCBsaXN0ID0gRkFMU0UpCnRyYWluX2RhdGEgPC0gaW1wdXRlZF9uaGFuZXNbdHJhaW5faW5kZXgsIF0KdGVzdF9kYXRhIDwtIGltcHV0ZWRfbmhhbmVzWy10cmFpbl9pbmRleCwgXQoKIyBSdW4gdGhlIGZ1bmN0aW9uCnN0ZXB3aXNlX3Jlc3VsdHMgPC0gc3RlcHdpc2VfYmljX21vZGVsKHRyYWluX2RhdGEsIHRlc3RfZGF0YSkKCgpgYGAKCgojIyBSaWRnZSBSZWdyZXNzaW9uCgpSaWRnZSByZWdyZXNzaW9uIGlzIGFwcGxpZWQgdG8gdGhlIE5IQU5FUyBkYXRhc2V0IHRvIGFkZHJlc3MgbXVsdGljb2xsaW5lYXJpdHkgYW1vbmcgcHJlZGljdG9yIHZhcmlhYmxlcyB3aGlsZSBtYWludGFpbmluZyBtb2RlbCBzdGFiaWxpdHkuIEJ5IGltcG9zaW5nIGEgcGVuYWx0eSBvbiBsYXJnZSBjb2VmZmljaWVudHMgdGhyb3VnaCBMMiByZWd1bGFyaXphdGlvbiwgUmlkZ2UgcmVncmVzc2lvbiBwcmV2ZW50cyBvdmVyZml0dGluZyBhbmQgaW1wcm92ZXMgZ2VuZXJhbGl6YXRpb24uIFRoZSBtb2RlbCB3YXMgZXZhbHVhdGVkIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24sIGFuZCBpdHMgcGVyZm9ybWFuY2Ugd2FzIGNvbXBhcmVkIHRvIG90aGVyIHJlZ3Jlc3Npb24gYXBwcm9hY2hlcyBiYXNlZCBvbiBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgYW5kIFLCsi4KCmBgYHtyfQoKIyBEZWZpbmUgcHJlZGljdG9yIG1hdHJpeCAoWCkgYW5kIHJlc3BvbnNlIHZhcmlhYmxlICh5KQpYIDwtIGFzLm1hdHJpeChpbXB1dGVkX25oYW5lc1ssIGMoImFnZSIsICJsb2dfQk1JIiwgImhlaWdodCIsICJsb2dfYm9keS53ZWlnaHQiLCAiYXZnLmRicCIsICJsb2dfYXZnLnNicCIpXSkKeSA8LSBpbXB1dGVkX25oYW5lcyRjaG9sZXN0cm9sCgpzZXQuc2VlZCgxMjMpICAjIEZvciByZXByb2R1Y2liaWxpdHkKCiMgRGVmaW5lIHRyYWluaW5nIGNvbnRyb2wgd2l0aCAxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24KdHJhaW5fY29udHJvbCA8LSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyID0gMTApCgojIFRyYWluIFJpZGdlIFJlZ3Jlc3Npb24gTW9kZWwKcmlkZ2VfbW9kZWwgPC0gdHJhaW4oCiAgeCA9IFgsIHkgPSB5LAogIG1ldGhvZCA9ICJnbG1uZXQiLAogIHRyQ29udHJvbCA9IHRyYWluX2NvbnRyb2wsCiAgdHVuZUdyaWQgPSBleHBhbmQuZ3JpZChhbHBoYSA9IDAsIGxhbWJkYSA9IDEwXnNlcSgtMywgMywgbGVuZ3RoID0gMTAwKSkgICMgUmlkZ2U6IGFscGhhID0gMAopCgojIEJlc3QgbGFtYmRhIHZhbHVlIHNlbGVjdGVkIHZpYSBjcm9zcy12YWxpZGF0aW9uCmJlc3RfbGFtYmRhIDwtIHJpZGdlX21vZGVsJGJlc3RUdW5lJGxhbWJkYQoKIyBQcmVkaWN0IHVzaW5nIGJlc3QgbGFtYmRhCnJpZGdlX3ByZWRpY3Rpb25zIDwtIHByZWRpY3QocmlkZ2VfbW9kZWwsIFgpCgojIENvbXB1dGUgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpCnJpZGdlX21zZSA8LSBtZWFuKCh5IC0gcmlkZ2VfcHJlZGljdGlvbnMpXjIpCgojIENvbXB1dGUgUsKyCnJzcyA8LSBzdW0oKHkgLSByaWRnZV9wcmVkaWN0aW9ucyleMikgICMgUmVzaWR1YWwgU3VtIG9mIFNxdWFyZXMKdHNzIDwtIHN1bSgoeSAtIG1lYW4oeSkpXjIpICAjIFRvdGFsIFN1bSBvZiBTcXVhcmVzCnJpZGdlX3IyIDwtIDEgLSAocnNzIC8gdHNzKSAgIyBSwrIgY2FsY3VsYXRpb24KCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZnJvbSB0aGUgZmluYWwgUmlkZ2UgbW9kZWwKY29lZmZpY2llbnRzIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KHJpZGdlX21vZGVsJGZpbmFsTW9kZWwkYmV0YSkpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJQcmVkaWN0b3IiKSAgIyBDb252ZXJ0IHJvdyBuYW1lcyB0byBhIGNvbHVtbgoKI3ByaW50KGthYmxlKGhlYWQoY29lZmZpY2llbnRzLCAxMCksIGRpZ2l0cyA9IDQsIGNhcHRpb24gPSAiVG9wIDEwIFJpZGdlIFJlZ3Jlc3Npb24gQ29lZmZpY2llbnRzIiwgZm9ybWF0ID0gIm1hcmtkb3duIikpCgoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIGZvciBNU0UsIFLCsiwgYW5kIEJlc3QgTGFtYmRhCm1ldHJpY3NfZGYgPC0gZGF0YS5mcmFtZSgKICBTdGF0aXN0aWMgPSBjKCJNZWFuIFNxdWFyZWQgRXJyb3IiLCAiUi1zcXVhcmVkIiwgIk9wdGltYWwgTGFtYmRhIiksCiAgVmFsdWUgPSBjKHJpZGdlX21zZSwgcmlkZ2VfcjIsIGJlc3RfbGFtYmRhKQopCgojIFByaW50IG1vZGVsIHBlcmZvcm1hbmNlIG1ldHJpY3MgdXNpbmcga2FibGUoKQpwcmludChrYWJsZShtZXRyaWNzX2RmLCBkaWdpdHMgPSA0LCBjYXB0aW9uID0gIlJpZGdlIFJlZ3Jlc3Npb24gUGVyZm9ybWFuY2UgTWV0cmljcyIsIGZvcm1hdCA9ICJtYXJrZG93biIpKQpgYGAKCgoKIyMgQ29tcGFyaXNvbgoKQWZ0ZXIgcGVyZm9ybWluZyBjcm9zcy12YWxpZGF0aW9uIG9uIHRoZSBOSEFORVNfaW1wdXRlZCBkYXRhc2V0LCB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBvZiB0aGUgdGhyZSBtb2RlbHMgYXJlIGNvbXBhcmVkLiBUaGUgQklDIG1vZGVsIGhhcyB0aGUgc21hbGxlc3QgTVNFIGFuZCBoaWdoZXN0IFJeMiB2YWx1ZSwgaW5kaWNhdGluZyBhIGJldHRlciBwZXJmb3JtaW5nIHJlZ3Jlc3Npb24gbW9kZWwuIEluLCB0dXJuLCB0aGUgQklDIG1vZGVsIGlzIHJlY29tbWVuZGVkLiAKCgoKCiMgTE9HSVNUSUMgUkVHUkVTU0lPTgoKVGhpcyBhbmFseXNpcyBhcHBsaWVzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gcHJlZGljdCBjdXJyZW50IHNtb2tpbmcgc3RhdHVzIHVzaW5nIGRhdGEgZnJvbSBOSEFORVMuIEZvdXIgbW9kZWxzIGFyZSBkZXZlbG9wZWQ6IGEgZnVsbCBtb2RlbCBpbmNsdWRpbmcgYWxsIHJlbGV2YW50IHByZWRpY3RvcnMsIGEgc3RlcHdpc2UgQUlDIG1vZGVsIGZvciB2YXJpYWJsZSBzZWxlY3Rpb24sIGEgQklDIG1vZGVsIGZvciBhIG1vcmUgcGFyc2ltb25pb3VzIGFwcHJvYWNoLCBhbmQgYSByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIExhc3NvIHRvIHByZXZlbnQgb3ZlcmZpdHRpbmcuIFRoZSBtb2RlbHMgYXJlIGV2YWx1YXRlZCBhbmQgY29tcGFyZWQgdXNpbmcgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljIChST0MpIGN1cnZlcyBhbmQgdGhlIEFyZWEgVW5kZXIgdGhlIEN1cnZlIChBVUMpIHRvIGFzc2VzcyB0aGVpciBjbGFzc2lmaWNhdGlvbiBwZXJmb3JtYW5jZSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeS4KCiMjIEZ1bGwgTW9kZWwKClRoZSBmaXJzdCBjYW5kaWRhdGUsIGEgZnVsbCBtb2RlbCwgaXMgZml0dGVkIHRvIHByZWRpY3QgY3VycmVudCBzbW9raW5nIHN0YXR1cy4gCgpgYGB7cn0KCiMgRml0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwKZnVsbF9tb2RlbCA8LSBnbG0oY3VycmVudC5zbWsgfiBhZ2UgKyByYWNlICsgbG9nX2JvZHkud2VpZ2h0ICsgbG9nX0JNSSArIG9iZXNlICsgCiAgICAgICAgICAgICAgICAgICAgaGVpZ2h0ICsgbG9nX2F2Zy5zYnAgKyBhdmcuZGJwICsgbG9nX2Nob2xlc3Ryb2wgKyBoYnAsIAogICAgICAgICAgICAgICAgICBkYXRhID0gaW1wdXRlZF9uaGFuZXMsIAogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbCkKCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZnJvbSB0aGUgbW9kZWwKY29lZmZpY2llbnRzIDwtIHN1bW1hcnkoZnVsbF9tb2RlbCkkY29lZmZpY2llbnRzCgojIENvbnZlcnQgY29lZmZpY2llbnRzIHRvIGEgZGF0YSBmcmFtZSBhbmQgYWRkIHJvdyBuYW1lcyBhcyBhIGNvbHVtbgpjb2VmZl90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKGNvZWZmaWNpZW50cykgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gIlByZWRpY3RvciIpICAjIENvbnZlcnQgcm93IG5hbWVzIHRvIGEgY29sdW1uCgojIFByaW50IHRoZSBjb2VmZmljaWVudHMgdGFibGUgdXNpbmcga2FibGUKa2FibGUoY29lZmZfdGFibGUsIGRpZ2l0cyA9IDQsIGNhcHRpb24gPSAiTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCBDb2VmZmljaWVudHMiLCBmb3JtYXQgPSAibWFya2Rvd24iKQoKCgpgYGAKCkFnZSwgcmFjZSwgYW5kIGF2ZXJhZ2UgZGJwIGFyZSBzaWduaWZpY2FudCBmZWF0dXJlcyBpbiB0aGUgZnVsbCBtb2RlbC4gCgojIyBTdGVwd2lzZSBBSUMgTW9kZWwKClRoZSBzZWNvbmQgbW9kZWwsIHN0ZXB3aXNlIEFJQywgaXMgZml0dGVkIHRvIHByZWRpY3QgY3VycmVudCBzbW9raW5nIHN0YXR1cy4gVGhpcyBtb2RlbCBhdXRvbWF0aWNhbGx5IHNlbGVjdHMgYSBzdWJzZXQgb2YgcHJlZGljdG9ycyB1c2luZyBBSUMtYmFzZWQgc3RlcHdpc2Ugc2VsZWN0aW9uLiAKCmBgYHtyfQoKCiMgRml0IGluaXRpYWwgZnVsbCBtb2RlbApmdWxsX21vZGVsIDwtIGdsbShjdXJyZW50LnNtayB+IGFnZSArIHJhY2UgKyBsb2dfYm9keS53ZWlnaHQgKyBsb2dfQk1JICsgb2Jlc2UgKyAKICAgICAgICAgICAgICAgICAgICBoZWlnaHQgKyBsb2dfYXZnLnNicCArIGF2Zy5kYnAgKyBsb2dfY2hvbGVzdHJvbCArIGhicCwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbXB1dGVkX25oYW5lcywgCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKQoKIyBQZXJmb3JtIHN0ZXB3aXNlIHNlbGVjdGlvbiB1c2luZyBBSUMKc3RlcHdpc2VfbW9kZWwgPC0gc3RlcEFJQyhmdWxsX21vZGVsLCBkaXJlY3Rpb24gPSAiYm90aCIsIHRyYWNlID0gRkFMU0UpCgojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZyb20gdGhlIHN0ZXB3aXNlIG1vZGVsCmNvZWZmaWNpZW50c19zdGVwd2lzZSA8LSBzdW1tYXJ5KHN0ZXB3aXNlX21vZGVsKSRjb2VmZmljaWVudHMKCiMgQ29udmVydCBjb2VmZmljaWVudHMgdG8gYSBkYXRhIGZyYW1lIGFuZCBhZGQgcm93IG5hbWVzIGFzIGEgY29sdW1uCmNvZWZmX3RhYmxlX3N0ZXB3aXNlIDwtIGFzLmRhdGEuZnJhbWUoY29lZmZpY2llbnRzX3N0ZXB3aXNlKSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUHJlZGljdG9yIikgICMgQ29udmVydCByb3cgbmFtZXMgdG8gYSBjb2x1bW4KCiMgUHJpbnQgdGhlIGNvZWZmaWNpZW50cyB0YWJsZSB1c2luZyBrYWJsZQprYWJsZShjb2VmZl90YWJsZV9zdGVwd2lzZSwgZGlnaXRzID0gNCwgY2FwdGlvbiA9ICJTdGVwd2lzZSBMb2dpc3RpYyBSZWdyZXNzaW9uIE1vZGVsIENvZWZmaWNpZW50cyIsIGZvcm1hdCA9ICJtYXJrZG93biIpCgoKCmBgYAoKQWxsIGZlYXR1cmVzLCBvdGhlciB0aGFuIG9iZXNlLCBhcmUgc2lnbmlmaWNhbnQgaW4gdGhlIHN0ZXB3aXNlIEFJQyBtb2RlbC4gCgoKIyMgQklDIE1vZGVsCgpUaGUgdGhpcmQgY2FuZGlkYXRlLGEgQklDIG1vZGVsLCBpcyBmaXR0ZWQgdG8gcHJlZGljdCBjdXJyZW50IHNtb2tpbmcgc3RhdHVzLiBUaGlzIG1vZGVsIGZpdHMgYSBtb3JlIHBhcnNpbW9uaW91cyBtb2RlbC4gCgpgYGB7cn0KCiMgRml0IGluaXRpYWwgZnVsbCBtb2RlbApmdWxsX21vZGVsIDwtIGdsbShjdXJyZW50LnNtayB+IGFnZSArIHJhY2UgKyBsb2dfYm9keS53ZWlnaHQgKyBsb2dfQk1JICsgb2Jlc2UgKyAKICAgICAgICAgICAgICAgICAgICBoZWlnaHQgKyBsb2dfYXZnLnNicCArIGF2Zy5kYnAgKyBsb2dfY2hvbGVzdHJvbCArIGhicCwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbXB1dGVkX25oYW5lcywgCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsKQoKIyBQZXJmb3JtIHN0ZXB3aXNlIHNlbGVjdGlvbiB1c2luZyBCSUMgKHVzaW5nIGsgPSBsb2cobnJvdyhpbXB1dGVkX25oYW5lcykpKQpiaWNfbW9kZWwgPC0gc3RlcEFJQyhmdWxsX21vZGVsLCBkaXJlY3Rpb24gPSAiYm90aCIsIGsgPSBsb2cobnJvdyhpbXB1dGVkX25oYW5lcykpLCB0cmFjZSA9IEZBTFNFKQoKIyBFeHRyYWN0IGNvZWZmaWNpZW50cyBmcm9tIHRoZSBCSUMgbW9kZWwKY29lZmZpY2llbnRzX2JpYyA8LSBzdW1tYXJ5KGJpY19tb2RlbCkkY29lZmZpY2llbnRzCgojIENvbnZlcnQgY29lZmZpY2llbnRzIHRvIGEgZGF0YSBmcmFtZSBhbmQgYWRkIHJvdyBuYW1lcyBhcyBhIGNvbHVtbgpjb2VmZl90YWJsZV9iaWMgPC0gYXMuZGF0YS5mcmFtZShjb2VmZmljaWVudHNfYmljKSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAiUHJlZGljdG9yIikgICMgQ29udmVydCByb3cgbmFtZXMgdG8gYSBjb2x1bW4KCiMgUHJpbnQgdGhlIGNvZWZmaWNpZW50cyB0YWJsZSB1c2luZyBrYWJsZQprYWJsZShjb2VmZl90YWJsZV9iaWMsIGRpZ2l0cyA9IDQsIGNhcHRpb24gPSAiQklDIFN0ZXB3aXNlIE1vZGVsIENvZWZmaWNpZW50cyIsIGZvcm1hdCA9ICJtYXJrZG93biIpCgoKYGBgCgpBbGwgZmVhdHVyZXMgYXJlIHNpZ25pZmljYW50IGluIHRoZSBCSUMgbW9kZWwuCgoKIyMgUmVndWxhcml6ZWQgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCAoTGFzc28pCgpUaGUgZm91cnRoIGNhbmRpZGF0ZSBtb2RlbCB1c2VzIFJlZ3VsYXJpemVkIExvZ2lzdGljIFJlZ3Jlc3Npb24gKExhc3NvKS4gVGhpcyBtZXRob2Qgc2VsZWN0cyB0aGUgbW9zdCBpbXBvcnRhbnQgcHJlZGljdG9ycy4KCmBgYHtyfQoKIyBEZWZpbmUgcHJlZGljdG9yIG1hdHJpeCAoWCkgYW5kIHJlc3BvbnNlIHZhcmlhYmxlICh5KQp4IDwtIG1vZGVsLm1hdHJpeChjdXJyZW50LnNtayB+IGFnZSArIHJhY2UgKyBsb2dfYm9keS53ZWlnaHQgKyBsb2dfQk1JICsgb2Jlc2UgKyBoZWlnaHQgKyBsb2dfYXZnLnNicCArIGF2Zy5kYnAgKyBsb2dfY2hvbGVzdHJvbCArIGhicCwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBpbXB1dGVkX25oYW5lcylbLCAtMV0gICMgRXhjbHVkZSBpbnRlcmNlcHQKeSA8LSBpbXB1dGVkX25oYW5lcyRjdXJyZW50LnNtawoKIyBUcmFpbiB0aGUgTGFzc28gbW9kZWwgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbgpsYXNzb19tb2RlbCA8LSBjdi5nbG1uZXQoeCwgeSwgZmFtaWx5ID0gImJpbm9taWFsIiwgYWxwaGEgPSAxKQoKCiMgQmVzdCBsYW1iZGEgdmFsdWUgc2VsZWN0ZWQgdmlhIGNyb3NzLXZhbGlkYXRpb24KYmVzdF9sYW1iZGFfbGFzc28gPC0gbGFzc29fbW9kZWwkbGFtYmRhLm1pbgpwcmludChwYXN0ZSgiT3B0aW1hbCBMYW1iZGEgZm9yIExhc3NvOiIsIGJlc3RfbGFtYmRhX2xhc3NvKSkKCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZnJvbSB0aGUgZmluYWwgTGFzc28gbW9kZWwgYXQgdGhlIG9wdGltYWwgbGFtYmRhCmxhc3NvX2NvZWZmaWNpZW50cyA8LSBjb2VmKGxhc3NvX21vZGVsLCBzID0gImxhbWJkYS5taW4iKQoKIyBDb252ZXJ0IGNvZWZmaWNpZW50cyB0byBhIGRhdGEgZnJhbWUKbGFzc29fY29lZmZpY2llbnRzX2RmIDwtIGFzLmRhdGEuZnJhbWUoYXMubWF0cml4KGxhc3NvX2NvZWZmaWNpZW50cykpCmxhc3NvX2NvZWZmaWNpZW50c19kZiRQcmVkaWN0b3IgPC0gcm93bmFtZXMobGFzc29fY29lZmZpY2llbnRzX2RmKQoKIyBSZW9yZGVyIGNvbHVtbnMgdG8gbWFrZSBwcmVkaWN0b3IgbmFtZXMgdGhlIGZpcnN0IGNvbHVtbgpsYXNzb19jb2VmZmljaWVudHNfZGYgPC0gbGFzc29fY29lZmZpY2llbnRzX2RmICU+JQogIHNlbGVjdChQcmVkaWN0b3IsIGV2ZXJ5dGhpbmcoKSkKCiMgUHJpbnQgdGhlIGNvZWZmaWNpZW50cyB0YWJsZSB1c2luZyBrYWJsZQprYWJsZShsYXNzb19jb2VmZmljaWVudHNfZGYsIGRpZ2l0cyA9IDQsIGNhcHRpb24gPSAiTGFzc28gUmVncmVzc2lvbiBDb2VmZmljaWVudHMiLCBmb3JtYXQgPSAibWFya2Rvd24iKQoKCmBgYAoKCiMjIENvbXBhcmlzb24KCkluIHRoaXMgYW5hbHlzaXMsIGZvdXIgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbHPigJRGdWxsIE1vZGVsLCBTdGVwd2lzZSBBSUMgTW9kZWwsIEJJQyBNb2RlbCwgYW5kIExhc3NvIE1vZGVsIGFyZSBjb21wYXJlZCB1c2luZyBST0MgKFJlY2VpdmVyIE9wZXJhdGluZyBDaGFyYWN0ZXJpc3RpYykgY3VydmVzIGFuZCBBVUMgKEFyZWEgVW5kZXIgdGhlIEN1cnZlKSB0byBhc3Nlc3MgdGhlaXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4gIFRoZSBvcHRpbWFsIGN1dG9mZiBmb3IgZWFjaCBtb2RlbCB1c2luZyBZb3VkZW7igJlzIEluZGV4IGlzIGRldGVybWluZWQsIHdoaWNoIG1heGltaXplcyB0aGUgYmFsYW5jZSBiZXR3ZWVuIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eS4gQnkgZXZhbHVhdGluZyB0aGVzZSBtb2RlbHMgdGhyb3VnaCBib3RoIGdyYXBoaWNhbCBhbmQgbnVtZXJpY2FsIG1ldHJpY3MsIGluc2lnaHRzIGlzIGdhaW5lZCBpbnRvIHdoaWNoIG1vZGVsIHByb3ZpZGVzIHRoZSBiZXN0IGNsYXNzaWZpY2F0aW9uIHBlcmZvcm1hbmNlIGZvciBwcmVkaWN0aW5nIHRoZSBvdXRjb21lIHZhcmlhYmxlLgoKYGBge3J9CiMgRnVsbCBNb2RlbApmdWxsX21vZGVsIDwtIGdsbShjdXJyZW50LnNtayB+IGFnZSArIHJhY2UgKyBsb2dfYm9keS53ZWlnaHQgKyBsb2dfQk1JICsgb2Jlc2UgKyBoZWlnaHQgKyBsb2dfYXZnLnNicCArIGF2Zy5kYnAgKyBsb2dfY2hvbGVzdHJvbCArIGhicCwgZGF0YSA9IGltcHV0ZWRfbmhhbmVzLCBmYW1pbHkgPSBiaW5vbWlhbCkKCiMgU3RlcHdpc2UgQUlDIE1vZGVsCnN0ZXB3aXNlX21vZGVsIDwtIHN0ZXBBSUMoZnVsbF9tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IEZBTFNFKQoKIyBCSUMgTW9kZWwKYmljX21vZGVsIDwtIHN0ZXBBSUMoZnVsbF9tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiLCBrID0gbG9nKG5yb3coaW1wdXRlZF9uaGFuZXMpKSwgdHJhY2UgPSBGQUxTRSkKCiMgTGFzc28gTW9kZWwKeCA8LSBtb2RlbC5tYXRyaXgoY3VycmVudC5zbWsgfiBhZ2UgKyByYWNlICsgbG9nX2JvZHkud2VpZ2h0ICsgbG9nX0JNSSArIG9iZXNlICsgaGVpZ2h0ICsgbG9nX2F2Zy5zYnAgKyBhdmcuZGJwICsgbG9nX2Nob2xlc3Ryb2wgKyBoYnAgLCBkYXRhID0gaW1wdXRlZF9uaGFuZXMpWywgLTFdICAjIEV4Y2x1ZGUgaW50ZXJjZXB0CnkgPC0gaW1wdXRlZF9uaGFuZXMkY3VycmVudC5zbWsKbGFzc29fY3YgPC0gY3YuZ2xtbmV0KHgsIHksIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMSkKbGFzc29fbW9kZWwgPC0gZ2xtbmV0KHgsIHksIGZhbWlseSA9ICJiaW5vbWlhbCIsIGFscGhhID0gMSwgbGFtYmRhID0gbGFzc29fY3YkbGFtYmRhLm1pbikKCiMgR2V0IHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzCmltcHV0ZWRfbmhhbmVzJGZ1bGxfcHJlZCA8LSBwcmVkaWN0KGZ1bGxfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKQppbXB1dGVkX25oYW5lcyRzdGVwd2lzZV9wcmVkIDwtIHByZWRpY3Qoc3RlcHdpc2VfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKQppbXB1dGVkX25oYW5lcyRiaWNfcHJlZCA8LSBwcmVkaWN0KGJpY19tb2RlbCwgdHlwZSA9ICJyZXNwb25zZSIpCmltcHV0ZWRfbmhhbmVzJGxhc3NvX3ByZWQgPC0gcHJlZGljdChsYXNzb19tb2RlbCwgbmV3eCA9IHgsIHR5cGUgPSAicmVzcG9uc2UiKVssMV0KCiMgQ3JlYXRlIFJPQyBjdXJ2ZXMKZnVsbF9yb2MgPC0gcm9jKGltcHV0ZWRfbmhhbmVzJGN1cnJlbnQuc21rLCBpbXB1dGVkX25oYW5lcyRmdWxsX3ByZWQpCnN0ZXB3aXNlX3JvYyA8LSByb2MoaW1wdXRlZF9uaGFuZXMkY3VycmVudC5zbWssIGltcHV0ZWRfbmhhbmVzJHN0ZXB3aXNlX3ByZWQpCmJpY19yb2MgPC0gcm9jKGltcHV0ZWRfbmhhbmVzJGN1cnJlbnQuc21rLCBpbXB1dGVkX25oYW5lcyRiaWNfcHJlZCkKbGFzc29fcm9jIDwtIHJvYyhpbXB1dGVkX25oYW5lcyRjdXJyZW50LnNtaywgaW1wdXRlZF9uaGFuZXMkbGFzc29fcHJlZCkKCiMgQ2FsY3VsYXRlIG9wdGltYWwgY3V0b2ZmIHVzaW5nIFlvdWRlbidzIEluZGV4Cm9wdGltYWxfY3V0b2ZmX2Z1bGwgPC0gZnVsbF9yb2Mkc3BlY2lmaWNpdGllc1t3aGljaC5tYXgoZnVsbF9yb2Mkc2Vuc2l0aXZpdGllcyArIGZ1bGxfcm9jJHNwZWNpZmljaXRpZXMgLSAxKV0Kb3B0aW1hbF9jdXRvZmZfc3RlcHdpc2UgPC0gc3RlcHdpc2Vfcm9jJHNwZWNpZmljaXRpZXNbd2hpY2gubWF4KHN0ZXB3aXNlX3JvYyRzZW5zaXRpdml0aWVzICsgc3RlcHdpc2Vfcm9jJHNwZWNpZmljaXRpZXMgLSAxKV0Kb3B0aW1hbF9jdXRvZmZfYmljIDwtIGJpY19yb2Mkc3BlY2lmaWNpdGllc1t3aGljaC5tYXgoYmljX3JvYyRzZW5zaXRpdml0aWVzICsgYmljX3JvYyRzcGVjaWZpY2l0aWVzIC0gMSldCm9wdGltYWxfY3V0b2ZmX2xhc3NvIDwtIGxhc3NvX3JvYyRzcGVjaWZpY2l0aWVzW3doaWNoLm1heChsYXNzb19yb2Mkc2Vuc2l0aXZpdGllcyArIGxhc3NvX3JvYyRzcGVjaWZpY2l0aWVzIC0gMSldCgojIFByaW50IG9wdGltYWwgY3V0b2ZmcwpjYXQoIk9wdGltYWwgY3V0b2ZmIGZvciBGdWxsIE1vZGVsOiIsIG9wdGltYWxfY3V0b2ZmX2Z1bGwsICJcbiIpCmNhdCgiT3B0aW1hbCBjdXRvZmYgZm9yIFN0ZXB3aXNlIEFJQyBNb2RlbDoiLCBvcHRpbWFsX2N1dG9mZl9zdGVwd2lzZSwgIlxuIikKY2F0KCJPcHRpbWFsIGN1dG9mZiBmb3IgQklDIE1vZGVsOiIsIG9wdGltYWxfY3V0b2ZmX2JpYywgIlxuIikKY2F0KCJPcHRpbWFsIGN1dG9mZiBmb3IgTGFzc28gTW9kZWw6Iiwgb3B0aW1hbF9jdXRvZmZfbGFzc28sICJcbiIpCgojIFBsb3QgYWxsIFJPQyBjdXJ2ZXMKZ2dwbG90KCkgKwogIGdlb21fbGluZShhZXMoeCA9IGZ1bGxfcm9jJHNwZWNpZmljaXRpZXMsIHkgPSBmdWxsX3JvYyRzZW5zaXRpdml0aWVzLCBjb2xvciA9ICJGdWxsIE1vZGVsIikpICsKICBnZW9tX2xpbmUoYWVzKHggPSBzdGVwd2lzZV9yb2Mkc3BlY2lmaWNpdGllcywgeSA9IHN0ZXB3aXNlX3JvYyRzZW5zaXRpdml0aWVzLCBjb2xvciA9ICJTdGVwd2lzZSBBSUMiKSkgKwogIGdlb21fbGluZShhZXMoeCA9IGJpY19yb2Mkc3BlY2lmaWNpdGllcywgeSA9IGJpY19yb2Mkc2Vuc2l0aXZpdGllcywgY29sb3IgPSAiQklDIE1vZGVsIikpICsKICBnZW9tX2xpbmUoYWVzKHggPSBsYXNzb19yb2Mkc3BlY2lmaWNpdGllcywgeSA9IGxhc3NvX3JvYyRzZW5zaXRpdml0aWVzLCBjb2xvciA9ICJMYXNzbyBNb2RlbCIpKSArCiAgbGFicyh0aXRsZSA9ICJST0MgQ3VydmUgQ29tcGFyaXNvbiIsIHggPSAiMSAtIFNwZWNpZmljaXR5IiwgeSA9ICJTZW5zaXRpdml0eSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicmVkIiwgImJsdWUiLCAiZ3JlZW4iLCAicHVycGxlIiwgIm9yYW5nZSIpKSArCiAgdGhlbWVfbWluaW1hbCgpCgojIEFVQyB2YWx1ZXMKYXVjX3ZhbHVlcyA8LSBkYXRhLmZyYW1lKAogIE1vZGVsID0gYygiRnVsbCBNb2RlbCIsICJTdGVwd2lzZSBBSUMiLCAiQklDIE1vZGVsIiwgIkxhc3NvIE1vZGVsIiksCiAgQVVDID0gYyhhdWMoZnVsbF9yb2MpLCBhdWMoc3RlcHdpc2Vfcm9jKSwgYXVjKGJpY19yb2MpLCBhdWMobGFzc29fcm9jKSkKKQoKcHJpbnQoYXVjX3ZhbHVlcykKYGBgCgpBbGwgbW9kZWxzIGhhdmUgdmVyeSBzaW1pbGFyIEFVQy4gSW4gdHVybiwgdGhlIG1vcmUgcGFyc2ltb25pb3VzIG1vZGVsLCBCSUMsIGlzIHJlY29tbWVuZGVkLiAKCiMgQ09OQ0xVU0lPTgoKSW4gdGhpcyBhbmFseXNpcywgYSBjb21wcmVoZW5zaXZlIGFwcHJvYWNoIHdhcyB0YWtlbiB0byBleHBsb3JlIGFuZCBtb2RlbCB0aGUgTkhBTkVTIGRhdGFzZXQuIEJlZ2lubmluZyB3aXRoIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSksIHBhdHRlcm5zIGFuZCBtaXNzaW5nIHZhbHVlcyB3ZXJlIGlkZW50aWZpZWQsIGxlYWRpbmcgdG8gdGhlIGltcGxlbWVudGF0aW9uIG9mIE11bHRpcGxlIEltcHV0YXRpb24gYnkgQ2hhaW5lZCBFcXVhdGlvbnMgKE1JQ0UpIHRvIGhhbmRsZSBtaXNzaW5nIGRhdGEuIEZlYXR1cmUgZW5naW5lZXJpbmcgYW5kIHRyYW5zZm9ybWF0aW9uIHdlcmUgYXBwbGllZCB0byBlbmhhbmNlIG1vZGVsIHBlcmZvcm1hbmNlLCBmb2xsb3dlZCBieSBmZWF0dXJlIHNlbGVjdGlvbiB0byByZWZpbmUgcHJlZGljdG9yIHZhcmlhYmxlcy4gQm90aCBsaW5lYXIgYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHdlcmUgZGV2ZWxvcGVkIGFuZCBldmFsdWF0ZWQsIGluY2x1ZGluZyBmdWxsIG1vZGVscywgc3RlcHdpc2Ugc2VsZWN0aW9uIG1ldGhvZHMsIGFuZCByZWd1bGFyaXplZCByZWdyZXNzaW9uIHRlY2huaXF1ZXMuIFVsdGltYXRlbHksIHRoZSBCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEJJQykgZW1lcmdlZCBhcyB0aGUgYmVzdC1wZXJmb3JtaW5nIG1vZGVsIGZvciBib3RoIGxpbmVhciBhbmQgbG9naXN0aWMgcmVncmVzc2lvbiwgZGVtb25zdHJhdGluZyBpdHMgZWZmZWN0aXZlbmVzcyBpbiBiYWxhbmNpbmcgbW9kZWwgY29tcGxleGl0eSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeS4K